var wtf;
var sortGraph;
/*
var typesJSON = {
    "http://purl.org/dc/dcmitype/StillImage" : {
        "http://www.w3.org/2000/01/rdf-schema#label" :[ {
            "value" : "Still Image", "type" : "literal"
        }
        ]
    },
};

var clone;
function test() {
    
    mainGraph.importJSON(tstJSON);
    typesGraph.importJSON(typesJSON);
}
*/


/* Core Objects */

function Graph() {
    this .subjects = {};

    
    this .size = function () {
        var count = 0;
        
        for (var s in this .subjects) {
            if (this .subjects [s] instanceof Subject) {
                count++;
            }
        }
        return count;
    };
    
    this .mergeGraph = function (graph) {
        for (var s in graph.subjects) {
            this .addSubject(graph.subjects[s]);
        }
    };
    
    this .removeSubject = function (sURI) {
        this .subjects[sURI] = null;
    };
    
    //this probably fails?
    this .getSubjectClone = function (sURI) {
        return new Subject(this .subjects[sURI].json);
    };
    
    this.getObjs4Pred = function(pred, asObjects) {
        pred = util.expandPrefix(pred);
        var oArray = new Array();
        for(var sURI in this.subjects) {
            oArray = oArray.concat(this.subjects[sURI].getObjs4Pred(pred, asObjects) );
        }
        return oArray;
    };
    
    this.extractSubjectsOfType = function (type, asArray) {
        type = util.expandPrefix(type);
        if(asArray == null) {
            asArray = true;
        }
        if(asArray) {
            var ret = new Array();
        } else {
            var ret = new Graph();
        }
        
        for (var sURI in this.subjects) {
            var classes = this.subjects[sURI].getClasses();
            if(  classes &&  classes.indexOf(type) != -1 ) {
                if(asArray) {
                    ret[ret.length] = this.subjects[sURI];
                } else {
                    ret.addSubject(this.subjects[sURI]);
                }
            }
        }
        return ret;
    },
    
    this .addSubject = function (s) {

        var subjectURI = s.subject;        
        if (this .subjects[subjectURI]) {         
            this .subjects[subjectURI].merge(s);
        } else {       
            this .subjects[subjectURI] = s;
        }
    };
    
    this .importJSON = function (json4import) {
        for (var s in json4import) {
            var subjectGraph = {
            };
            subjectGraph[s] = json4import[s];
            var newSubject = new Subject(subjectGraph);            
            this .addSubject(newSubject);
        }
    };
    
    this .listSubjects = function () {
        var string = '';
        for (var s in this .subjects) {
            string += s + '\n';
        }
        return string;
    };
    
    this .toNT = function () {
        nt = '';
        for (var subject in this .subjects) {
            
            if (this .subjects[subject] instanceof Subject) {
                nt += this .subjects[subject].toNT();
            }
        }
        return nt;
    };
   
   
   /*****************************
    *
    *  Sorting functions
    *
     **************************/
   
    this.sortByLabel= function () {
        sortGraph = this; 
        var newSubjects = {};
        var subjectOrderArray = new Array();
        for (var s in this.subjects) {
            subjectOrderArray[subjectOrderArray.length] = s;
            
        }
        
       subjectOrderArray.sort(this.sortByLabelFunc);
       for (var sIndex = 0; sIndex < subjectOrderArray.length; sIndex++ ) {
       //alert(subjectOrderArray.sIndex);
           newSubjects[subjectOrderArray[ sIndex ]] = this.subjects[subjectOrderArray[ sIndex ] ];       
       }
       
       this. subjects = newSubjects;
    };
    
    this.sortByLabelFunc = function sortByLabelFunc(a, b) {
                var aLabel = sortGraph.subjects[a].getTitle();
                var bLabel = sortGraph.subjects[b].getTitle();
                if (aLabel < bLabel) {
                    return -1;            
                }
                if (aLabel > bLabel) {
                    return 1;
                }
                if(aLabel == bLabel) {
                    return 0;
                }
   };
    

    
    
    this.pseudoInfer = function () {
        for (var s in this.subjects) {
           this.mergeGraph( this.subjects[s].pseudoInfer() ) ;
        }
    };
}


function Subject(initfo) {
    this .isSaved = false;
    //looks like a subjectURI
    if (typeof initfo == 'string') {

        this .json = {
        };
        this .json[initfo] = {
        };
        this .subject = initfo;
    } else {
        
        this .json = initfo;
        //TODO:  really should throw an error if more than one subject
        for (var sURI in this .json) {
            this .subject = sURI;
        }
    }
    
    this.resetURI = function(uri) {
        this.subject = uri;
        this.json = eval( "( { '" + uri + "' : {} }  ) " ) ;
    
    };
    
    this .toNT = function () {
        var ntSubject = ' <' + this .subject + '> ';
        var nt = '';
        for (var pred in this .json[ this .subject]) {
            var ntPred = ' <' + pred + '> ';
            for (var i = 0; i < this .json[ this .subject][pred].length; i++) {
                
                nt += ntSubject + ntPred;
                if (this .json[ this .subject][pred][i].type == 'literal') {
                    nt += '"' + this .json[ this .subject][pred][i].value.replace('"', "'", 'g') + '"';
                } else {
                    nt += '<' + this .json[ this .subject][pred][i].value + '>';
                }
                if (this .json[ this .subject][pred][i].lang) {
                    nt += '@' + this .json[ this .subject][pred][i].lang;
                }
                
                if (this .json[ this .subject][pred][i].datatype) {
                    nt += '^^' + this .json[ this .subject][pred][i].datatype;
                }
                nt += ' . \n';
            }
        }
        return nt;
    };
    
    this .setSaved = function (issaved) {
        this .isSaved = issaved;
    };
    
    this .hasPred = function (pred) {
        var pred = util.expandPrefix(pred);
        for (var p in this .json[ this .subject]) {
            if (p == pred) return true;
        }
        return false;
    };
    
    this .getClasses = function (classURI) {
        var pred = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type';
        return this .getObjs4Pred(pred);
    };
    
    this .addClass = function (classURI) {
        var pred = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type';
        var obj = util.makeURI(classURI);
        this .addPO(pred, obj);
    };
    
    this .addPO = function (pred, obj) {
       //if obj is a Subject (as used here), turn it into an object
       if(obj instanceof Subject) {
           obj = util.subjectToObject(obj);
       }       
        if( this.hasPO(pred, obj.value)) {
      
            return;
        }
        pred = util.expandPrefix(pred);
        
        if (this .json[ this .subject][pred]) {
      
            if (! this.hasPO(pred, obj.value) ) {
                this .json[ this .subject][pred][ this .json[ this .subject][pred].length] = obj;
            }
        } else {
            this .json[ this .subject][pred] = new Array();
            this .json[ this .subject][pred][ this .json[ this .subject][pred].length] = obj;
        }
    };
    
    this .addPredArray = function (pred, predArray) {
        this .json[ this .subject][pred] = predArray;
    }
    
    
    this .hasPO = function (pred, oVal) {
        pred = util.expandPrefix(pred);
        oVal = util.expandPrefix(oVal);
        for (var p in this .json[ this .subject]) {
            if (p == pred) {
                for (var i = 0; i < this .json[ this .subject][p].length; i++) {
                    if (this .json[ this .subject][p][i].value == oVal) {
                        return true;
                    }
                }
            }
        }
        return false;
    };
    
    this.hasObject = function(oVal) {
        for(var pred in this.json[this.subject]) {
            for (var oIndex in this.json[this.subject][pred]) {
                if(this.json[this.subject][pred][oIndex].value == oVal) {
                    return true;
                }
            }
        
        
        }
    return false;
    };
    
    this .hasPred = function (pred) {
        pred = util.expandPrefix(pred);
        for (var p in this .json[ this .subject]) {
            if (p == pred) {
                return true;
            }
        }
        return false;
    };
    
    this .removePO = function (pred, obj) {
       //if obj is a Subject (as used here), turn it into an object
       if(obj instanceof Subject) {
           obj = util.subjectToObject(obj);
       }        
        pred = util.expandPrefix(pred);
        if (obj.type == 'uri') {
            obj.value = util.expandPrefix(obj.value);
        }
        for (var i = 0; i < this .json[ this .subject][pred].length; i++) {
            
            if (this .json[ this .subject][pred][i].value == obj.value) {
                
                this .json[ this .subject][pred].splice(i, 1);
            }
        }
    };
    
    this .replacePO = function (pred, oldObj, newObj) {
        
        pred = util.expandPrefix(pred);
        
        if (oldObj.type == 'uri') {
            oldObj.value = util.expandPrefix(oldObj.value);
        }
        if (newObj.type == 'uri') {
            newObj.value = util.expandPrefix(newObj.value);
        }
        
        this .removePO(pred, oldObj);
        this .addPO(pred, newObj);
    };
    
    this .getObjs4Pred = function (pred, asObjects) {
        pred = util.expandPrefix(pred);
        if( ! this.hasPred(pred)) {
            return false;
        }
        var oArray = new Array();
        for (var i = 0; i < this .json[ this .subject][pred].length; i++) {
            if(asObjects == true) {
                oArray[oArray.length] = this .json[ this .subject][pred][i];
            } else {
               oArray[oArray.length] = this .json[ this .subject][pred][i].value;
            }
        }
      return oArray;
    };
    
    this.getURIObjects = function() {
        var urisArray = new Array();
        for (var pred in this.json[this.subject] ) {
            for (var i = 0; i < this.json[this.subject][pred].length; i++ ) {
                if ( this.json[this.subject][pred][i].type == 'uri' ) {                 
                    urisArray[urisArray.length] = this.json[this.subject][pred][i].value; 
                }            
            }        
        }
     return urisArray;
    };
    
    this.getTitle = function() {
        if (this.hasPred('http://purl.org/dc/elements/1.1/title') ) {
            //go through objects checking for lang set to 'en'
            for(var i = 0; i< this .json[ this .subject]['http://purl.org/dc/elements/1.1/title'].length; i++) {
                if ( this .json[ this .subject]['http://purl.org/dc/elements/1.1/title'][i].lang &&  this .json[ this .subject]['http://purl.org/dc/elements/1.1/title'][i].lang == 'en') {
                    return this .json[ this .subject]['http://purl.org/dc/elements/1.1/title'][i].value;
                }
            }
            // default assume that the lang is en
            return this .json[ this .subject]['http://purl.org/dc/elements/1.1/title'][0].value;
        }
        if (this.hasPred('http://purl.org/dc/terms/title') ) {
            //go through objects checking for lang set to 'en'
            for(var i = 0; i< this .json[ this .subject]['http://purl.org/dc/terms/title'].length; i++) {
                if ( this .json[ this .subject]['http://purl.org/dc/terms/title'][i].lang &&  this .json[ this .subject]['http://purl.org/dc/terms/title'][i].lang == 'en') {
                    return this .json[ this .subject]['http://purl.org/dc/terms/title'][i].value;
                }
            }
            // default assume that the lang is en        
            return this .json[ this .subject]['http://purl.org/dc/terms/title'][0].value;
        }        

        if (this.hasPred('foaf:name') ) {
            //go through objects checking for lang set to 'en'
            for(var i = 0; i< this .json[ this .subject]['http://xmlns.com/foaf/0.1/name'].length; i++) {
                if ( this .json[ this .subject]['http://xmlns.com/foaf/0.1/name'][i].lang &&  this .json[ this .subject]['http://xmlns.com/foaf/0.1/name'][i].lang == 'en') {
                    return this .json[ this .subject]['http://xmlns.com/foaf/0.1/name'][i].value;
                }
            }
            // default assume that the lang is en     
            return this .json[ this .subject]['http://xmlns.com/foaf/0.1/name'][0].value;
        }   

        if (this.hasPred('http://www.w3.org/2000/01/rdf-schema#label') ) {
            //go through objects checking for lang set to 'en'
            for(var i = 0; i< this .json[ this .subject]['http://www.w3.org/2000/01/rdf-schema#label'].length; i++) {
                if ( this .json[ this .subject]['http://www.w3.org/2000/01/rdf-schema#label'][i].lang &&  this .json[ this .subject]['http://www.w3.org/2000/01/rdf-schema#label'][i].lang == 'en') {
                    return this .json[ this .subject]['http://www.w3.org/2000/01/rdf-schema#label'][i].value;
                }
            }
            // default assume that the lang is en             
            return this .json[ this .subject]['http://www.w3.org/2000/01/rdf-schema#label'][0].value;
        }            

        if (this.hasPred('skos:prefLabel') ) {
        
            //go through objects checking for lang set to 'en'
            for(var i = 0; i< this .json[ this .subject]['http://www.w3.org/2004/02/skos/core#prefLabel'].length; i++) {
                if ( this .json[ this .subject]['http://www.w3.org/2004/02/skos/core#prefLabel'][i].lang &&  this .json[ this .subject]['http://www.w3.org/2004/02/skos/core#prefLabel'][i].lang == 'en') {
                    return this .json[ this .subject]['http://www.w3.org/2004/02/skos/core#prefLabel'][i].value;
                }
            }
            // default assume that the lang is en               
        
        
            return this .json[ this .subject]['http://www.w3.org/2004/02/skos/core#prefLabel'][0].value;
        }   

        
    }
    
    
    //TODO:  need to update other methods for new subject/path structure
    this .getFirstObj4Pred = function (pred, asObject) {
        pred = util.expandPrefix(pred);
        if(this.hasPred(pred) ) {
            if(asObject == true) {
                return this .json[ this .subject][pred][0];
            } else {
                return this .json[ this .subject][pred][0].value;
            }
        } else {
            return false;
        }
    };
    
    this .merge = function (subjectObj) {
            if(this === subjectObj) {
                return true;
            }
        //first, check that it is same subjectURI
        if (this .subject !== subjectObj.subject) {
            return false;
        }
        //for each predicate, if not in this add it
        for (var p in subjectObj.json[subjectObj.subject]) {
            if (! this .hasPred(p)) {
                
                this .addPredArray(p, subjectObj.json[subjectObj.subject][p]);
            } else {
             

                for (var oIndex = 0;  oIndex < subjectObj.json[ this .subject][p].length; oIndex++) {
                  this.addPO(p, subjectObj.json[ this .subject][p][oIndex] );                    
                }
            }
        }
        return true;
    };
    
    this.pseudoInfer = function() {
    
        var inferredGraph = new Graph();
        //this just goes through a hard-coded set of predicates to add the appropriate types and super predicates
        
        // geg:studiesTopic
        var arr = this.getObjs4Pred('geg:studiesTopic', true);

        for (var i = 0 ; i< arr.length; i++) {
            var newS = new Subject(arr[i].value);
            newS.addPO('rdf:type', util.makeURI('geg:StudyTopic') );
            newS.addPO('rdf:type', util.makeURI('geg:StudyThing') );
            this.addPO('geg:studyThing', arr[i]);
            inferredGraph.addSubject(newS);   
            
        }
        
        // geg:studiesWithPractice
        var arr = this.getObjs4Pred('geg:studiesWithPractice', true);
        for (var i = 0 ; i< arr.length; i++) {
            var newS = new Subject(arr[i].value );
            newS.addPO('rdf:type', util.makeURI('geg:StudyPractice') );
            newS.addPO('rdf:type', util.makeURI('geg:StudyThing') );
            this.addPO('geg:studyThing', arr[i]);
            inferredGraph.addSubject(newS);            
        }

        
        // geg:studiesByMeansOf
        var arr = this.getObjs4Pred('geg:studiesByMeansOf', true);
        for (var i = 0 ; i< arr.length; i++) {
            var newS = new Subject(arr[i].value);
            newS.addPO('rdf:type', util.makeURI('geg:StudyMeans') );
            newS.addPO('rdf:type', util.makeURI('geg:StudyThing') );
            this.addPO('geg:studyThing', arr[i]);
            inferredGraph.addSubject(newS);            
        }

        
        // geg:studiesFromPerspective
        var arr = this.getObjs4Pred('geg:studiesFromPerspective', true);
        for (var i = 0 ; i< arr.length; i++) {
            var newS = new Subject(arr[i] .value);
            newS.addPO('rdf:type', util.makeURI('geg:StudyPerspective') );
            newS.addPO('rdf:type', util.makeURI('geg:StudyThing') );
            this.addPO('geg:studyThing', arr[i]);
            inferredGraph.addSubject(newS);
        }
        
        return inferredGraph;

    };
    
   
}



/* Init */

var mainGraph = new Graph();

var typesGraph = new Graph();
