#import <Foundation/Foundation.h>
#import <Sparksee/Sparksee.h>



int main(int argc, const char * argv[])
{
    @autoreleasepool {

        //
        // Create a sample database
        //
        STSSparkseeConfig *cfg = [[STSSparkseeConfig alloc] init];
        //[cfg setLicense: @"THE_LICENSE_KEY"];
        STSSparksee *sparksee = [[STSSparksee alloc] initWithConfig: cfg];
        // If you are not using Objective-C Automatic Reference Counting, you
        // may want to release the configuration here.
        //[cfg release];
        STSDatabase *db = [sparksee create: @"HelloSparksee.gdb" alias: @"HelloSparksee"];
        STSSession *sess = [db createSession];
        STSGraph *g = [sess getGraph];

        //
        // SCHEMA
        //

        // Add a node type for the movies, with a unique identifier and two indexed attributes
        int movieType = [g createNodeType: @"MOVIE"];
        int movieIdType = [g createAttribute: movieType name: @"ID" dt: STSLong kind: STSUnique];
        int movieTitleType = [g createAttribute: movieType name: @"TITLE" dt: STSString kind: STSIndexed];
        int movieYearType = [g createAttribute: movieType name: @"YEAR" dt: STSInteger kind: STSIndexed];

        // Add a node type for the people, with a unique identifier and an indexed attribute
        int peopleType = [g createNodeType: @"PEOPLE"];
        int peopleIdType = [g createAttribute: peopleType name: @"ID" dt: STSLong kind: STSUnique];
        int peopleNameType = [g createAttribute: peopleType name: @"NAME" dt: STSString kind: STSIndexed];

        // Add an undirected edge type with an attribute for the cast of a movie
        int castType = [g createEdgeType: @"CAST" directed: FALSE neighbors: FALSE];
        int castCharacterType = [g createAttribute: castType name: @"CHARACTER"dt: STSString kind: STSBasic];

        // Add a directed edge type restricted to go from people to movie for the director of a movie
        int directsType = [g createRestrictedEdgeType: @"DIRECTS" tail: peopleType head: movieType neighbors: FALSE];

        //
        // DATA
        //

        // Add some MOVIE nodes
        STSValue *value = [[STSValue alloc] init];

        long long mLostInTranslation = [g createNode: movieType];
        [g setAttribute: mLostInTranslation attr: movieIdType value: [value setLong: 1]];
        [g setAttribute: mLostInTranslation attr: movieTitleType value: [value setString: @"Lost in Translation"]];
        [g setAttribute: mLostInTranslation attr: movieYearType value: [value setInteger: 2003]];

        long long mVickyCB = [g createNode: movieType];
        [g setAttribute: mVickyCB attr: movieIdType value: [value setLong: 2]];
        [g setAttribute: mVickyCB attr: movieTitleType value: [value setString: @"Vicky Cristina Barcelona"]];
        [g setAttribute: mVickyCB attr: movieYearType value: [value setInteger: 2008]];

        long long mManhattan = [g createNode: movieType];
        [g setAttribute: mManhattan attr: movieIdType value: [value setLong: 3]];
        [g setAttribute: mManhattan attr: movieTitleType value: [value setString: @"Manhattan"]];
        [g setAttribute: mManhattan attr: movieYearType value: [value setInteger: 1979]];


        // Add some PEOPLE nodes
        long long pScarlett = [g createNode: peopleType];
        [g setAttribute: pScarlett attr: peopleIdType value: [value setLong: 1]];
        [g setAttribute: pScarlett attr: peopleNameType value: [value setString: @"Scarlett Johansson"]];

        long long pBill = [g createNode: peopleType];
        [g setAttribute: pBill attr: peopleIdType value: [value setLong: 2]];
        [g setAttribute: pBill attr: peopleNameType value: [value setString: @"Bill Murray"]];

        long long pSofia = [g createNode: peopleType];
        [g setAttribute: pSofia attr: peopleIdType value: [value setLong: 3]];
        [g setAttribute: pSofia attr: peopleNameType value: [value setString: @"Sofia Coppola"]];

        long long pWoody = [g createNode: peopleType];
        [g setAttribute: pWoody attr: peopleIdType value: [value setLong: 4]];
        [g setAttribute: pWoody attr: peopleNameType value: [value setString: @"Woody Allen"]];

        long long pPenelope = [g createNode: peopleType];
        [g setAttribute: pPenelope attr: peopleIdType value: [value setLong: 5]];
        [g setAttribute: pPenelope attr: peopleNameType value: [value setString: @"Penélope Cruz"]];

        long long pDiane = [g createNode: peopleType];
        [g setAttribute: pDiane attr: peopleIdType value: [value setLong: 6]];
        [g setAttribute: pDiane attr: peopleNameType value: [value setString: @"Diane Keaton"]];



        // Add some CAST edges
        long long anEdge;
        anEdge = [g createEdge: castType tail: mLostInTranslation head: pScarlett];
        [g setAttribute: anEdge attr: castCharacterType value: [value setString: @"Charlotte"]];

        anEdge = [g createEdge: castType tail: mLostInTranslation head: pBill];
        [g setAttribute: anEdge attr: castCharacterType value: [value setString: @"Bob Harris"]];

        anEdge = [g createEdge: castType tail: mVickyCB head: pScarlett];
        [g setAttribute: anEdge attr: castCharacterType value: [value setString: @"Cristina"]];

        anEdge = [g createEdge: castType tail: mVickyCB head: pPenelope];
        [g setAttribute: anEdge attr: castCharacterType value: [value setString: @"Maria Elena"]];

        anEdge = [g createEdge: castType tail: mManhattan head: pDiane];
        [g setAttribute: anEdge attr: castCharacterType value: [value setString: @"Mary"]];

        anEdge = [g createEdge: castType tail: mManhattan head: pWoody];
        [g setAttribute: anEdge attr: castCharacterType value: [value setString: @"Isaac"]];



        // Add some DIRECTS edges
        anEdge = [g createEdge: directsType tail: pSofia head: mLostInTranslation];

        anEdge = [g createEdge: directsType tail: pWoody head: mVickyCB];

        anEdge = [g createEdge: directsType tail: pWoody head: mManhattan];



        //
        // QUERIES
        //

        // Get the movies directed by Woody Allen
        STSObjects *directedByWoody = [g neighbors: pWoody etype: directsType dir: STSOutgoing];

        // Get the cast of the movies directed by Woody Allen
        STSObjects *castDirectedByWoody = [g neighborsWithObjects: directedByWoody etype: castType dir: STSAny];

        // We don't need the directedByWoody collection anymore, so we should close it
        [directedByWoody close];

        // Get the movies directed by Sofia Coppola
        STSObjects *directedBySofia = [g neighbors: pSofia etype: directsType dir: STSOutgoing];

        // Get the cast of the movies directed by Sofia Coppola
        STSObjects *castDirectedBySofia = [g neighborsWithObjects: directedBySofia etype: castType dir: STSAny];

        // We don't need the directedBySofia collection anymore, so we should close it
        [directedBySofia close];

        // We want to know the people that acted in movies directed by Woody AND in movies directed by Sofia.
        STSObjects *castFromBoth = [STSObjects combineIntersection: castDirectedByWoody objs2: castDirectedBySofia];

        // We don't need the other collections anymore
        [castDirectedByWoody close];
        [castDirectedBySofia close];

        // Say hello to the people found
        STSObjectsIterator *it = [castFromBoth iterator];
        while ([it hasNext])
        {
            long long peopleOid = [it next];
            [g getAttributeInValue: peopleOid attr: peopleNameType value: value];
            NSLog(@"Hello %@\n", [value getString]);
        }
        // The ObjectsIterator must be closed
        [it close];

        // The Objects must be closed
        [castFromBoth close];

        // The value is not needed anymore
        // If you are not using Objective-C Automatic Reference Counting, you
        // may want to release it here.
        //[value release];


        //
        // Close the database
        //
        [sess close];
        [db close];
        [sparksee close];
        // If you are not using Objective-C Automatic Reference Counting, you
        // may want to release the sparksee here, when it's closed.
        //[sparksee release];
    }
    return 0;
}

