
var Thrift = require('../nodejs/thrift-node');

// Define types and services
var TestNext = {};

TestNext.Numberz = {
    'ONE': 1,
    'TWO': 2,
    'THREE': 3,
    'FIVE': 5,
    'SIX': 6,
    'EIGHT': 8
};

TestNext.Bonk = Thrift.Struct.define('Bonk', {
    1: { alias: 'thing', type: Thrift.Type.STRING }
});

TestNext.MapType = Thrift.Map.define('MapType', Thrift.Type.STRING, Thrift.Type.STRUCT, TestNext.Bonk);

TestNext.Bools = Thrift.Struct.define('Bools', {
    1: { alias: 'im_true', type: Thrift.Type.BOOL },
    2: { alias: 'im_false', type: Thrift.Type.BOOL }
});

TestNext.Xtruct = Thrift.Struct.define('Xtruct', {
    1: { alias: 'string_thing', type: Thrift.Type.STRING },
    4: { alias: 'byte_thing', type: Thrift.Type.BYTE },
    9: { alias: 'i32_thing', type: Thrift.Type.I32 },
    11: { alias: 'i64_thing', type: Thrift.Type.I64 }
});

TestNext.Xtruct2 = Thrift.Struct.define('Xtruct2', {
    1: { alias: 'byte_thing', type: Thrift.Type.BYTE },
    2: { alias: 'struct_thing', type: Thrift.Type.STRUCT, def: TestNext.Xtruct },
    3: { alias: 'i32_thing', type: Thrift.Type.I32 }
});

TestNext.Xtruct3 = Thrift.Struct.define('Xtruct3', {
    1: { alias: 'string_thing', type: Thrift.Type.STRING },
    4: { alias: 'changed', type: Thrift.Type.I32 },
    9: { alias: 'i32_thing', type: Thrift.Type.I32 },
    11: { alias: 'i64_thing', type: Thrift.Type.I64 }
});

TestNext.Insanity = Thrift.Struct.define('Insanity', {
    1: { alias: 'userMap', type: Thrift.Type.MAP, 
            def: Thrift.Map.define('userMap', Thrift.Type.I32, Thrift.Type.I64) },
    2: { alias: 'xtructs', type: Thrift.Type.LIST, 
            def: Thrift.List.define(Thrift.Type.STRUCT, TestNext.Xtruct) }
});

// CrazyNesting is not supported because it uses a Set as the key for a Map

TestNext.Xception = Thrift.Exception.define('Xception', {
    1: { alias: 'errorCode', type: Thrift.Type.I32 },
    2: { alias: 'message', type: Thrift.Type.STRING }
});

TestNext.Xception2 = Thrift.Exception.define('Xception', {
    1: { alias: 'errorCode', type: Thrift.Type.I32 },
    2: { alias: 'struct_thing', type: Thrift.Type.STRUCT, def: TestNext.Xtruct }
});

TestNext.EmptyStruct = Thrift.Struct.define('EmptyStruct');

TestNext.ThriftTest = {};

TestNext.ThriftTest.testVoid = Thrift.Method.define({
    alias: 'testVoid',
    args: Thrift.Struct.define('testVoidArgs'),
    result: Thrift.Struct.define('testVoidResult')
});

TestNext.ThriftTest.testString = Thrift.Method.define({
    alias: 'testString',
    args: Thrift.Struct.define('testStringArgs', {
        1: { alias: 'thing', type: Thrift.Type.STRING }
    }),
    result: Thrift.Struct.define('testStringResult', {
        0: { alias: 'returnValue', type: Thrift.Type.STRING }
    })
});

TestNext.ThriftTest.testByte = Thrift.Method.define({
    alias: 'testByte',
    args: Thrift.Struct.define('testByteArgs', {
        1: { alias: 'thing', type: Thrift.Type.BYTE }
    }),
    result: Thrift.Struct.define('testByteResult', {
        0: { alias: 'returnValue', type: Thrift.Type.BYTE }
    })
});

TestNext.ThriftTest.testI32 = Thrift.Method.define({
    alias: 'testI32',
    args: Thrift.Struct.define('testI32Args', {
        1: { alias: 'thing', type: Thrift.Type.I32 }
    }),
    result: Thrift.Struct.define('testI32Result', {
        0: { alias: 'returnValue', type: Thrift.Type.I32 }
    })
});

TestNext.ThriftTest.testI64 = Thrift.Method.define({
    alias: 'testI64',
    args: Thrift.Struct.define('testI64Args', {
        1: { alias: 'thing', type: Thrift.Type.I64 }
    }),
    result: Thrift.Struct.define('testI64Result', {
        0: { alias: 'returnValue', type: Thrift.Type.I64 }
    })
});

TestNext.ThriftTest.testDouble = Thrift.Method.define({
    alias: 'testDouble',
    args: Thrift.Struct.define('testDoubleArgs', {
        1: { alias: 'thing', type: Thrift.Type.DOUBLE }
    }),
    result: Thrift.Struct.define('testDoubleResult', {
        0: { alias: 'returnValue', type: Thrift.Type.DOUBLE }
    })
});

TestNext.ThriftTest.testStruct = Thrift.Method.define({
    alias: 'testStruct',
    args: Thrift.Struct.define('testStructArgs', {
        1: { alias: 'thing', type: Thrift.Type.STRUCT, def: TestNext.Xtruct }
    }),
    result: Thrift.Struct.define('testStructResult', {
        0: { alias: 'returnValue', type: Thrift.Type.STRUCT, def: TestNext.Xtruct }
    })
});

TestNext.ThriftTest.testNest = Thrift.Method.define({
    alias: 'testNest',
    args: Thrift.Struct.define('testNestArgs', {
        1: { alias: 'thing', type: Thrift.Type.STRUCT, def: TestNext.Xtruct2 }
    }),
    result: Thrift.Struct.define('testNestResult', {
        0: { alias: 'returnValue', type: Thrift.Type.STRUCT, def: TestNext.Xtruct2 }
    })
});

TestNext.ThriftTest.testMap = Thrift.Method.define({
    alias: 'testMap',
    args: Thrift.Struct.define('testMapArgs', {
        1: { alias: 'thing', type: Thrift.Type.MAP, 
                def: Thrift.Map.define(Thrift.Type.I32, Thrift.Type.I32) }
    }),
    result: Thrift.Struct.define('testMapResult', {
        0: { alias: 'returnValue', type: Thrift.Type.MAP, 
                def: Thrift.Map.define(Thrift.Type.I32, Thrift.Type.I32) }
    })
});

TestNext.ThriftTest.testStringMap = Thrift.Method.define({
    alias: 'testStringMap',
    args: Thrift.Struct.define('testStringMapArgs', {
        1: { alias: 'thing', type: Thrift.Type.MAP, 
                def: Thrift.Map.define(Thrift.Type.STRING, Thrift.Type.STRING) }
    }),
    result: Thrift.Struct.define('testStringMapResult', {
        0: { alias: 'returnValue', type: Thrift.Type.MAP, 
                def: Thrift.Map.define(Thrift.Type.STRING, Thrift.Type.STRING) }
    })
});

TestNext.ThriftTest.testSet = Thrift.Method.define({
    alias: 'testSet',
    args: Thrift.Struct.define('testSetArgs', {
        1: { alias: 'thing', type: Thrift.Type.SET, def: Thrift.Set.define(Thrift.Type.I32) }
    }),
    result: Thrift.Struct.define('testSetResult', {
        0: { alias: 'returnValue', type: Thrift.Type.SET, def: Thrift.Set.define(Thrift.Type.I32) }
    })
});

TestNext.ThriftTest.testList = Thrift.Method.define({
    alias: 'testList',
    args: Thrift.Struct.define('testListArgs', {
        1: { alias: 'thing', type: Thrift.Type.LIST, def: Thrift.List.define(Thrift.Type.I32) }
    }),
    result: Thrift.Struct.define('testListResult', {
        0: { alias: 'returnValue', type: Thrift.Type.LIST, def: Thrift.List.define(Thrift.Type.I32) }
    })
});

TestNext.ThriftTest.testEnum = Thrift.Method.define({
    alias: 'testEnum',
    args: Thrift.Struct.define('testEnumArgs', {
        1: { alias: 'thing', type: Thrift.Type.I32 }
    }),
    result: Thrift.Struct.define('testEnumResult', {
        0: { alias: 'returnValue', type: Thrift.Type.I32 }
    })
});

TestNext.ThriftTest.testTypedef = Thrift.Method.define({
    alias: 'testTypedef',
    args: Thrift.Struct.define('testTypedefArgs', {
        1: { alias: 'thing', type: Thrift.Type.I64 }
    }),
    result: Thrift.Struct.define('testTypedefResult', {
        0: { alias: 'returnValue', type: Thrift.Type.I64 }
    })
});

TestNext.ThriftTest.testMapMap = Thrift.Method.define({
    alias: 'testMapMap',
    args: Thrift.Struct.define('testMapMapArgs', {
        1: { alias: 'hello', type: Thrift.Type.I32 }
    }),
    result: Thrift.Struct.define('testMapMapResult', {
        0: { alias: 'returnValue', type: Thrift.Type.MAP, 
                def: Thrift.Map.define(
                    Thrift.Type.I32, Thrift.Type.MAP, Thrift.Map.define(
                        Thrift.Type.I32,  Thrift.Type.I32
                    )
                )
            }
    })
});

TestNext.ThriftTest.testException = Thrift.Method.define({
    alias: 'testException',
    args: Thrift.Struct.define('testExceptionArgs', {
        1: { alias: 'arg', type: Thrift.Type.STRING }
    }),
    result: Thrift.Struct.define('testExceptionResult', {
        1: { alias: 'err1', type: Thrift.Type.EXCEPTION, def: TestNext.Xception }
    })
});

TestNext.ThriftTest.testInsanity = Thrift.Method.define({
    alias: 'testInsanity',
    args: Thrift.Struct.define('testInsanityArgs', {
        1: { alias: 'argument', type: Thrift.Type.STRUCT, def: TestNext.Insanity }
    }),
    result: Thrift.Struct.define('testInsanityResult', {
        0: { alias: 'returnValue', type: Thrift.Type.MAP, 
                def: Thrift.Map.define(
                    Thrift.Type.I64, Thrift.Type.MAP, Thrift.Map.define(
                        Thrift.Type.I32, Thrift.Type.STRUCT, TestNext.Insanity
                    )
                )
            }
    })
});

// Define ThriftTest Service Client
(function (namespace) {
    var Message = Thrift.Message;
    var ServiceDef = namespace.ThriftTest;

    function Client(output) {
        this.output = output;
        this.seqid = 0;
    }

    Client.prototype.testVoid = function (onsuccess, onerror) {
        var mdef = ServiceDef.testVoid;
        var args = new mdef.args();
        mdef.sendRequest(this.output, this.seqid++, args, onsuccess, onerror);
    };

    Client.prototype.testString = function (thing, onsuccess, onerror) {
        var mdef = ServiceDef.testString;
        var args = new mdef.args();
        args.thing = thing;
        mdef.sendRequest(this.output, this.seqid++, args, onsuccess, onerror);
    };

    Client.prototype.testByte = function (thing, onsuccess, onerror) {
        var mdef = ServiceDef.testByte;
        var args = new mdef.args();
        args.thing = thing;
        mdef.sendRequest(this.output, this.seqid++, args, onsuccess, onerror);
    };

    Client.prototype.testI32 = function (thing, onsuccess, onerror) {
        var mdef = ServiceDef.testI32;
        var args = new mdef.args();
        args.thing = thing;
        mdef.sendRequest(this.output, this.seqid++, args, onsuccess, onerror);
    };

    Client.prototype.testI64 = function (thing, onsuccess, onerror) {
        var mdef = ServiceDef.testI64;
        var args = new mdef.args();
        args.thing = thing;
        mdef.sendRequest(this.output, this.seqid++, args, onsuccess, onerror);
    };

    Client.prototype.testDouble = function (thing, onsuccess, onerror) {
        var mdef = ServiceDef.testDouble;
        var args = new mdef.args();
        args.thing = thing;
        mdef.sendRequest(this.output, this.seqid++, args, onsuccess, onerror);
    };

    Client.prototype.testStruct = function (thing, onsuccess, onerror) {
        var mdef = ServiceDef.testStruct;
        var args = new mdef.args();
        args.thing = thing;
        mdef.sendRequest(this.output, this.seqid++, args, onsuccess, onerror);
    };

    Client.prototype.testNest = function (thing, onsuccess, onerror) {
        var mdef = ServiceDef.testNest;
        var args = new mdef.args();
        args.thing = thing;
        mdef.sendRequest(this.output, this.seqid++, args, onsuccess, onerror);
    };

    Client.prototype.testMap = function (thing, onsuccess, onerror) {
        var mdef = ServiceDef.testMap;
        var args = new mdef.args();
        args.thing = thing;
        mdef.sendRequest(this.output, this.seqid++, args, onsuccess, onerror);
    };

    Client.prototype.testStringMap = function (thing, onsuccess, onerror) {
        var mdef = ServiceDef.testStringMap;
        var args = new mdef.args();
        args.thing = thing;
        mdef.sendRequest(this.output, this.seqid++, args, onsuccess, onerror);
    };

    Client.prototype.testSet = function (thing, onsuccess, onerror) {
        var mdef = ServiceDef.testSet;
        var args = new mdef.args();
        args.thing = thing;
        mdef.sendRequest(this.output, this.seqid++, args, onsuccess, onerror);
    };

    Client.prototype.testList = function (thing, onsuccess, onerror) {
        var mdef = ServiceDef.testList;
        var args = new mdef.args();
        args.thing = thing;
        mdef.sendRequest(this.output, this.seqid++, args, onsuccess, onerror);
    };

    Client.prototype.testEnum = function (thing, onsuccess, onerror) {
        var mdef = ServiceDef.testEnum;
        var args = new mdef.args();
        args.thing = thing;
        mdef.sendRequest(this.output, this.seqid++, args, onsuccess, onerror);
    };

    Client.prototype.testTypedef = function (thing, onsuccess, onerror) {
        var mdef = ServiceDef.testTypedef;
        var args = new mdef.args();
        args.thing = thing;
        mdef.sendRequest(this.output, this.seqid++, args, onsuccess, onerror);
    };

    Client.prototype.testMapMap = function (hello, onsuccess, onerror) {
        var mdef = ServiceDef.testMapMap;
        var args = new mdef.args();
        args.hello = hello;
        mdef.sendRequest(this.output, this.seqid++, args, onsuccess, onerror);
    };

    Client.prototype.testException = function (arg, onsuccess, onerror) {
        var mdef = ServiceDef.testException;
        var args = new mdef.args();
        args.arg = arg;
        mdef.sendRequest(this.output, this.seqid++, args, onsuccess, onerror);
    };

    Client.prototype.testInsanity = function (argument, onsuccess, onerror) {
        var mdef = ServiceDef.testInsanity;
        var args = new mdef.args();
        args.argument = argument;
        mdef.sendRequest(this.output, this.seqid++, args, onsuccess, onerror);
    };

    ServiceDef.Client = Client;
})(TestNext);


(function (namespace) {
    var Message = Thrift.Message;
    var ServiceDef = namespace.ThriftTest;

    function Server(service, stransport, Protocol, async) {
        var methodName;

        async = (async == null) ? true : async;
        this.service = service;
        this.stransport = stransport;
        this.processor = new Thrift.Processor();

        for (methodName in ServiceDef) {
            if (service[methodName]) {
                this.processor.addMethod(ServiceDef[methodName], service[methodName].bind(service), async);
            }
        }

        this.stransport.process = function (input, output) {
            this.processor.process(new Protocol(input), new Protocol(output));
        }.bind(this);
    }

    Server.prototype.start = function () {
        this.stransport.listen();
    };

    Server.prototype.stop = function () {
        this.stransport.close();
    };

    ServiceDef.Server = Server;
})(TestNext);

// Node exports
Object.keys(TestNext).forEach(function (key) {
    exports[key] = TestNext[key];
});

