//
// Copyright (c) ZeroC, Inc. All rights reserved.
//

#import <objc/Ice.h>
#import <TestCommon.h>
#import <ExceptionsTest.h>

@interface ExceptionsEmptyI : TestExceptionsEmpty<TestExceptionsEmpty>
@end

@implementation ExceptionsEmptyI
@end

id<TestExceptionsThrowerPrx>
exceptionsAllTests(id<ICECommunicator> communicator)
{
    tprintf("testing object adapter registration exceptions... ");
    {
        id<ICEObjectAdapter> first;
        @try
        {
            first = [communicator createObjectAdapter:@"TestAdapter0"];
            test(false);
        }
        @catch(ICEInitializationException *ex)
        {
            // Expeccted
        }

        [[communicator getProperties] setProperty:@"TestAdapter0.Endpoints" value:@"default"];
        first = [communicator createObjectAdapter:@"TestAdapter0"];
        @try
        {
            [communicator createObjectAdapter:@"TestAdapter0"];
            test(false);
        }
        @catch(ICEAlreadyRegisteredException *ex)
        {
            // Expected
        }

        @try
        {
        }
        @catch(ICEAlreadyRegisteredException *ex)
        {
            // Expected
        }

        //
        // Properties must remain unaffected if an exception occurs.
        //
        test([[[communicator getProperties] getProperty:@"TestAdapter0.Endpoints"] isEqualToString:@"default"]);
        [first deactivate];
    }
    tprintf("ok\n");

    tprintf("testing servant registration exceptions... ");
    {
        [[communicator getProperties] setProperty:@"TestAdapter1.Endpoints" value:@"default"];
        id<ICEObjectAdapter> adapter = [communicator createObjectAdapter:@"TestAdapter1"];
        ICEObject* obj = [ExceptionsEmptyI empty];
        [adapter add:obj identity:[ICEUtil stringToIdentity:@"x"]];
        @try
        {
            [adapter add:obj identity:[ICEUtil stringToIdentity:@"x"]];
            test(false);
        }
        @catch(ICEAlreadyRegisteredException *ex)
        {
        }

        [adapter remove:[ICEUtil stringToIdentity:@"x"]];
        @try
        {
            [adapter remove:[ICEUtil stringToIdentity:@"x"]];
            test(false);
        }
        @catch(ICENotRegisteredException *ex)
        {
        }

        [adapter deactivate];
    }
    tprintf("ok\n");

    tprintf("testing stringToProxy... ");
    NSString *ref = [NSString stringWithFormat:@"thrower:%@", getTestEndpoint(communicator, 0)];
    id<ICEObjectPrx> base = [communicator stringToProxy:ref];
    test(base);
    tprintf("ok\n");

    tprintf("testing checked cast... ");
    id<TestExceptionsThrowerPrx> thrower = [TestExceptionsThrowerPrx checkedCast:base];
    test(thrower);
    test([thrower isEqual:base]);
    tprintf("ok\n");

    tprintf("catching exact types... ");

    @try
    {
        [thrower throwAasA:1];
        test(false);
    }
    @catch(TestExceptionsA *ex)
    {
        test(ex.aMem == 1);
    }
    @catch(NSException *ex)
    {
        test(false);
    }

    @try
    {
        [thrower throwAorDasAorD:1];
        test(false);
    }
    @catch(TestExceptionsA *ex)
    {
        test(ex.aMem == 1);
    }
    @catch(NSException *ex)
    {
        test(false);
    }

    @try
    {
        [thrower throwAorDasAorD:-1];
        test(false);
    }
    @catch(TestExceptionsD *ex)
    {
        test(ex.dMem == -1);
    }
    @catch(NSException* ex)
    {
        test(false);
    }

    @try
    {
        [thrower throwBasB:1 b:2];
        test(false);
    }
    @catch(TestExceptionsB *ex)
    {
        test(ex.aMem == 1);
        test(ex.bMem == 2);
    }
    @catch(NSException *ex)
    {
        test(false);
    }

    @try
    {
        [thrower throwCasC:1 b:2 c:3];
        test(false);
    }
    @catch(TestExceptionsC *ex)
    {
        test(ex.aMem == 1);
        test(ex.bMem == 2);
        test(ex.cMem == 3);
    }
    @catch(NSException *ex)
    {
        test(false);
    }

    @try
    {
        [thrower throwModA:1 a2:2];
        test(false);
    }
    @catch(TestExceptionsModA *ex)
    {
        test(ex.aMem == 1);
        test(ex.a2Mem == 2);
    }
    @catch(ICEOperationNotExistException *ex)
    {
        //
        // This operation is not supported in Java.
        //
    }
    @catch(NSException *ex)
    {
        test(false);
    }

    tprintf("ok\n");

    tprintf("catching base types... ");

    @try
    {
        [thrower throwBasB:1 b:2];
        test(false);
    }
    @catch(TestExceptionsA *ex)
    {
        test(ex.aMem == 1);
    }
    @catch(NSException *ex)
    {
        test(false);
    }

    @try
    {
        [thrower throwCasC:1 b:2 c:3];
        test(false);
    }
    @catch(TestExceptionsB *ex)
    {
        test(ex.aMem == 1);
        test(ex.bMem == 2);
    }
    @catch(NSException *ex)
    {
        test(false);
    }

    @try
    {
        [thrower throwModA:1 a2:2];
        test(false);
    }
    @catch(TestExceptionsA *ex)
    {
        test(ex.aMem == 1);
    }
    @catch(ICEOperationNotExistException *ex)
    {
        //
        // This operation is not supported in Java.
        //
    }
    @catch(NSException *ex)
    {
        test(false);
    }

    tprintf("ok\n");

    tprintf("catching derived types... ");

    @try
    {
        [thrower throwBasA:1 b:2];
        test(false);
    }
    @catch(TestExceptionsB *ex)
    {
        test(ex.aMem == 1);
        test(ex.bMem == 2);
    }
    @catch(NSException *ex)
    {
        test(false);
    }

    @try
    {
        [thrower throwCasA:1 b:2 c:3];
        test(false);
    }
    @catch(TestExceptionsC *ex)
    {
        test(ex.aMem == 1);
        test(ex.bMem == 2);
        test(ex.cMem == 3);
    }
    @catch(NSException *ex)
    {
        test(false);
    }

    @try
    {
        [thrower throwCasB:1 b:2 c:3];
        test(false);
    }
    @catch(TestExceptionsC *ex)
    {
        test(ex.aMem == 1);
        test(ex.bMem == 2);
        test(ex.cMem == 3);
    }
    @catch(NSException *ex)
    {
        test(false);
    }

    tprintf("ok\n");

    if([thrower supportsUndeclaredExceptions])
    {
        tprintf("catching unknown user exception... ");

        @try
        {
            [thrower throwUndeclaredA:1];
            test(false);
        }
        @catch(ICEUnknownUserException *ex)
        {
        }
        @catch(NSException *ex)
        {
            NSLog(@"%@", ex);
            test(false);
        }

        @try
        {
            [thrower throwUndeclaredB:1 b:2];
            test(false);
        }
        @catch(ICEUnknownUserException *ex)
        {
        }
        @catch(NSException *ex)
        {
            test(false);
        }

        @try
        {
            [thrower throwUndeclaredC:1 b:2 c:3];
            test(false);
        }
        @catch(ICEUnknownUserException *ex)
        {
        }
        @catch(NSException *ex)
        {
            test(false);
        }

        tprintf("ok\n");
    }

    if([thrower ice_getConnection])
    {
        tprintf("testing memory limit marshal exception...");
        @try
        {
            ICEByteSeq *bs = [NSMutableData dataWithLength:0];
            [thrower throwMemoryLimitException:bs];
            test(false);
        }
        @catch(ICEMemoryLimitException *ex)
        {
        }
        @catch(NSException *ex)
        {
            test(false);
        }

        @try
        {
            [thrower throwMemoryLimitException:[NSMutableData dataWithLength:20 * 1024]]; // 20KB
            test(false);
        }
        @catch(ICEConnectionLostException *ex)
        {
        }
        @catch(ICEUnknownLocalException *ex)
        {
        }
        @catch(NSException *ex)
        {
            test(false);
        }

        @try
        {
            id<TestExceptionsThrowerPrx> thrower2 =
                [TestExceptionsThrowerPrx checkedCast:[communicator stringToProxy:@"thrower:default -p 12011"]];
            @try
            {
                [thrower2 throwMemoryLimitException:[NSMutableData dataWithLength:20 * 1024 * 1024]]; // 2MB (no limits)
            }
            @catch(ICEMemoryLimitException *ex)
            {
            }
            id<TestExceptionsThrowerPrx> thrower3 =
                [TestExceptionsThrowerPrx checkedCast:[communicator stringToProxy:@"thrower:default -p 12012"]];
            @try
            {
                [thrower3 throwMemoryLimitException:[NSMutableData dataWithLength:1024]]; // 1KB limit
                test(NO);
            }
            @catch(ICEConnectionLostException *ex)
            {
            }
        }
        @catch(ICEConnectionRefusedException* ex)
        {
        }
        tprintf("ok\n");
    }

    tprintf("catching object not exist exception... ");

    ICEIdentity *id_ = [ICEUtil stringToIdentity:@"does not exist"];
    @try
    {
        id<TestExceptionsThrowerPrx> thrower2 = [TestExceptionsThrowerPrx uncheckedCast:[thrower ice_identity:id_]];
        [thrower2 throwAasA:1];
//      //[thrower2 ice_ping];
        test(false);
    }
    @catch(ICEObjectNotExistException *ex)
    {
        test([ex.id_ isEqual:id_]);
    }
    @catch(NSException *ex)
    {
        test(false);
    }

    tprintf("ok\n");

    tprintf("catching facet not exist exception... ");

    @try
    {
        id<TestExceptionsThrowerPrx> thrower2 = [TestExceptionsThrowerPrx uncheckedCast:thrower facet:@"no such facet"];
        @try
        {
            [thrower2 ice_ping];
            test(false);
        }
        @catch(ICEFacetNotExistException *ex)
        {
            test([ex.facet isEqualToString:@"no such facet"]);
        }
    }
    @catch(NSException *ex)
    {
        test(false);
    }

    tprintf("ok\n");

    tprintf("catching operation not exist exception... ");

    @try
    {
        id<TestExceptionsWrongOperationPrx> thrower2 = [TestExceptionsWrongOperationPrx uncheckedCast:thrower];
        [thrower2 noSuchOperation];
        test(false);
    }
    @catch(ICEOperationNotExistException *ex)
    {
        test([ex.operation isEqualToString:@"noSuchOperation"]);
    }
    @catch(NSException *ex)
    {
        test(false);
    }

    tprintf("ok\n");

    tprintf("catching unknown local exception... ");

    @try
    {
        [thrower throwLocalException];
        test(false);
    }
    @catch(ICEUnknownLocalException *ex)
    {
    }
    @catch(NSException *ex)
    {
        test(false);
    }
    @try
    {
        [thrower throwLocalExceptionIdempotent];
        test(false);
    }
    @catch(ICEUnknownLocalException*)
    {
    }
    @catch(ICEOperationNotExistException*)
    {
    }
    @catch(NSException* ex)
    {
        test(false);
    }

    tprintf("ok\n");

    tprintf("catching unknown non-Ice exception... ");

    @try
    {
        [thrower throwNonIceException];
        test(false);
    }
    @catch(ICEUnknownException *)
    {
    }
    @catch(NSException *)
    {
        test(false);
    }

    tprintf("ok\n");

    return thrower;
}
