import json
import os
from django.db import models
from jsonfield import JSONField
#from django.contrib.postgres.fields import ArrayField
#from reqman.apps.common.models import CoreModel
from reqman.apps.account.models import User
from reqman.apps.reqtool.models.main_req import * # create_main, ITEM_CHOICES, STATE_VALUE_CHOICES, STATE_CHOICES, SYSTEM_CHOICES, FUNCTION_CHOICES, FLOW_CHOICES
from reqman.apps.reqtool.models.suffix_req import * #create_suffix
from reqman.apps.reqtool.rest_api.services.parse_ontologies import *

from pygments.formatters.html import HtmlFormatter
from pygments import highlight
from subprocess import call
from reqman.settings.environment import env


#get prefix syntax from the Onotlogy#
#print(get_prefix_new_syntax())
#PREFIX_SYNTAX = get_prefix_syntax()


SIMPLE_PREFIX_CHOICES = (   ("",""),  
                            ("P1", "If/Unless <logical expression>"), 
                            ("P2", "As soon as <occuring functionality>"), 
                            ("P3", "As long as <occuring functionality>")
                        )

LOGIC_CONNECTIVE = (    ("or","OR"), 
                        ("and", "AND"), 
                        ("xor", "XOR")
                    )

LOGICAL_EXPRESSION = (  ("",""), 
                        ("state value constraint", "state value constraint"), 
                        ("occuring functionality", "occuring functionality")
                    )

#Need fixing
#VERB_CHOICES_PREFIX = (  ("",""))

list_of_RBO = ["http://delab.csd.auth.gr/ontologies/2018/LO#","http://delab.csd.auth.gr/ontologies/2018/RBO#","http://delab.csd.auth.gr/ontologies/2018/LO-instances#"]


#Must read this info from the Ontology
PREFIX_CHOICES = (   ("If", "If"), ("Unless", "Unless"),
                            ("As soon as", "As soon as"), 
                            ("As long as", "As long as"), ("Once", "Once")
                        )


def create_prefixgroup(instance):
    '''Create the PrefixGroup Model for the Boilerplate'''
    try :
        prefix = PrefixGroup.objects.get(boilerplate = instance, prefix_owner = instance.owner)
        if instance.has_prefix:
            return
        prefix = PrefixGroup.objects.get(boilerplate = instance, prefix_owner = instance.owner)
        prefix.delete()
    except PrefixGroup.DoesNotExist:
        if instance.has_prefix:
            prefix = PrefixGroup(boilerplate = instance, prefix_owner = instance.owner)
            prefix.save()


def create_prefix(instance):
        prefix = Prefix(prefix_boilerplate = instance.boilerplate, prefixgroup = instance)
        prefix.save()


def create_data(instance):
        data = BoilerplateData(boilerplate_data_id = instance, owner_data = instance.owner, title_data= instance.title, group_of_boilerplate_data = instance.group_of_boilerplate )
        data.save()


def create_extra_prefix(instance, now_prefix):
    prefix = Prefix(prefixgroup = instance.prefixgroup, prefix_boilerplate = instance.prefix_boilerplate)
    prefix.save()


def create_infer_result(instance, destination):
    infer = InferenceResults(owner_infer= instance.boilerplate_owner, infer_group_of_boilerplate = instance, ontology_file=destination)
    infer.save()
    #Here we have to give and the ontology file of the Boilerplate Group to get the current instances

def initialize_instances(instance): 
    try : 
        g = Graph()
        file_name = instance.title_bgroup.replace('/', '')
        save_path = "../../Ontologies/"+file_name+"/"
        if (os.path.exists("../../Ontologies/"+file_name+"/") == False):
            os.mkdir("../../Ontologies/"+file_name)
            completeName = os.path.join(save_path, file_name)
        if (instance.ontology_file != '../../Ontologies/Default'):
            ont = instance.ontology_file.replace('../../Ontologies', '')
            call("./../../Ontologies/s-get "+ env("FUSEKI_URL")+ ont +" default >> ../../Ontologies/"+file_name+'/'+file_name+'.ttl', shell=True)
            destination=save_path+file_name+'.ttl'
        else :
            g.load('../../Ontologies/2022_AOCS.ttl', format="turtle")
            destination=save_path+file_name+'.ttl'
            g.serialize(destination, format='turtle')
    except:
        print('Cannot load Ontology')
    classes_inctances = BoilerplateGroupClassesInstances ( classes_instances_owner= instance.boilerplate_owner, classes_instances_group_of_boilerplate = instance,
    ontology_file= destination)
    classes_inctances.save()
    dict = BoilerplateGroupDictionary( classes_instances_owner_dic= instance.boilerplate_owner, classes_instances_group_of_boilerplate_dic = instance,
    ontology_file_dic= destination)
    dict.save()
    return destination

    '''Update instaces of the ontology after the inferencing'''
def update_instances(instance): 
    list_of_DSO=[]
    g = Graph()
    g.load(instance.ontology_file, format="turtle")
    for ns_prefix, namespace in g.namespaces():
        if ('http://delab.csd.auth.gr/ontologies/2018/DSO' in namespace):
            list_of_DSO.append(str(namespace))
    list_of_DSO.append("http://delab.csd.auth.gr/ontologies/2018/SAO#")
    list_of_DSO.append("http://delab.csd.auth.gr/ontologies/2018/RDO-instances#")
    #PREFIX_SYNTAX = get_prefix_syntax(g)
    VERB_CHOICES_PREFIX = get_prefix_verbs(g)
    VERB_CHOICES = get_instances_g("LO#Action", g, ["http://delab.csd.auth.gr/ontologies/2018/LO#"])
    SYSTEM_CHOICES = get_instances_g("SAO#System",g, list_of_DSO)
    FUNCTION_CHOICES = get_instances_g("SAO#Function",g, list_of_DSO)
    ITEM_CHOICES = get_instances_g("SAO#Item",g, list_of_DSO)
    INTERFACE_CHOICES = get_instances_g("SAO#Interface",g, list_of_DSO)
    CONNECTION_CHOICES = get_instances_g("SAO#Connection",g, list_of_DSO)
    FLOW_CHOICES = get_instances_g("SAO#Flow",g, list_of_DSO)
    STATE_CHOICES = get_instances_g("SAO#State",g, list_of_DSO)
    STATE_SET_CHOICES = get_instances_g("SAO#StateSet",g, list_of_DSO)
    STATE_VALUE_CHOICES =get_instances_g("SAO#StateValue",g, list_of_DSO)
    NUMBER_UNITS_CHOICES = get_instances_g("RBO#NumberUnit",g, list_of_RBO)
    QUANTIFIER_CHOICES = get_instances_g("RBO#Quantifier",g, list_of_RBO)
    NUMERICAL_AFFIRMATIVE_CHOICES = get_instances_g("RBO#NumericalAffirmative",g, list_of_RBO)
    S_CHOICES = get_instances_g("RBO#Preposition",g, list_of_RBO) + get_instances_g("RBO#NumericalAffirmative",g, list_of_RBO)
    BoilerplateGroupClassesInstances.objects.filter(classes_instances_owner= instance.owner_infer, classes_instances_group_of_boilerplate = instance.infer_group_of_boilerplate).update(
    prefix_choices = json.dumps(PREFIX_CHOICES), prefix_logcon_choices =json.dumps(LOGIC_CONNECTIVE), prefix_verb_choices =json.dumps(STATE_CHOICES + VERB_CHOICES_PREFIX),
    main_class_choices =json.dumps(MAIN_CHOICES), system_choices = json.dumps(SYSTEM_CHOICES), function_choices =json.dumps(FUNCTION_CHOICES),
    quantifier_choices=json.dumps(QUANTIFIER_CHOICES), number_unit_choices=json.dumps(NUMBER_UNITS_CHOICES), item_choices=json.dumps(ITEM_CHOICES),
    inteface_choices=json.dumps(INTERFACE_CHOICES), connection_choices=json.dumps(CONNECTION_CHOICES), flow_choices=json.dumps(FLOW_CHOICES),
    state_choices=json.dumps(STATE_CHOICES),state_set_choices=json.dumps(STATE_SET_CHOICES), state_value_choices=json.dumps(STATE_VALUE_CHOICES),
    main_verb_choices=json.dumps(VERB_CHOICES), numerical_affirmative_choices=json.dumps(NUMERICAL_AFFIRMATIVE_CHOICES),
    suffix_choices=json.dumps(S_CHOICES), ontology_file=instance.ontology_file )
    Dict_list=[]
    sao_names=['SAO#System', 'SAO#Function', 'SAO#Item', 'SAO#Interface', 'SAO#Connection', 'SAO#Flow', 'SAO#State', 'SAO#StateSet', 'SAO#StateValue']
    for i in sao_names:
        Dict_list.append(get_dmo_classes_and_comment(i, g, list_of_DSO))
    #Dictionary_Data_list.append
    BoilerplateGroupDictionary.objects.filter(classes_instances_owner_dic= instance.owner_infer, classes_instances_group_of_boilerplate_dic = instance.infer_group_of_boilerplate).update(ontology_file_dic =instance.ontology_file, Dictionary_Data =Dict_list)




class BoilerplateGroup(models.Model):
    boilerplate_owner = models.ForeignKey(User, related_name='boilerplate_group_owner', on_delete=models.CASCADE)
    #add Boilerplate path, so each Boilerplate Group can hace it's own Ontology file
   # ontology_file = models.CharField(default="../../Ontologies/2022_AOCS.ttl", max_length=100, blank=True)
    ontology_file = models.CharField(max_length=100, blank=True)
    title_bgroup = models.CharField(max_length=100, unique=True)
    description = models.CharField(max_length=500)

    def save(self, **kwargs):
        super(BoilerplateGroup, self).save(**kwargs)
        destination = initialize_instances(self)
        create_infer_result(self, destination)



class Boilerplate(models.Model):
    owner = models.ForeignKey(User, related_name='boilerplate', on_delete=models.CASCADE)
    group_of_boilerplate = models.ForeignKey(BoilerplateGroup, related_name='owner_of_boilerplate', on_delete=models.CASCADE)
    created = models.DateTimeField(auto_now_add=True)
    title = models.CharField(max_length=300, unique=True)
    has_prefix = models.BooleanField(default=False)
    has_main = models.BooleanField(default=False)
    has_suffix = models.BooleanField(default=False)


    def __init__(self, *args, **kwargs):
            super(Boilerplate, self).__init__(*args, **kwargs)
            self.__important_fields = ['has_prefix', 'has_main', 'has_suffix']
            for field in self.__important_fields:
                setattr(self, '__original_%s' % field, getattr(self, field))
        

    def save(self, **kwargs):
        """Create prefix, main or suffix """
        super(Boilerplate, self).save(**kwargs)
        prev_prefix_list = []
        for field in self.__important_fields:
            orig = '__original_%s' % field
            previous_simple_prefix = getattr(self, orig)
            prev_prefix_list.append(previous_simple_prefix)
        create_prefixgroup(self)
        create_main(self)
        create_suffix(self)
        if (prev_prefix_list[0] == True and self.has_prefix == False ):
            pr = ""
            BoilerplateData.objects.filter(owner_data=self.owner, boilerplate_data_id = self).update(prefix_data = pr )
        elif (prev_prefix_list[1] == True and self.has_main == False ):
            pr = ""
            BoilerplateData.objects.filter(owner_data=self.owner, boilerplate_data_id = self).update(main_data = pr )
        elif (prev_prefix_list[2] == True and self.has_suffix == False ):
            pr = ""
            BoilerplateData.objects.filter(owner_data=self.owner, boilerplate_data_id = self).update(suffix_data = pr )
        elif(BoilerplateData.objects.filter(owner_data=self.owner, boilerplate_data_id = self).count() != 1):
            create_data(self)

        
    class Meta:
        ordering = ['created']


class PrefixGroup(models.Model):
    boilerplate = models.ForeignKey(Boilerplate, related_name='prefixgroup', on_delete=models.CASCADE)
    prefix_owner = models.ForeignKey(User, related_name='prefix_group_owner', on_delete=models.CASCADE)

    def save(self, **kwargs):
        super(PrefixGroup, self).save(**kwargs)
        create_prefix(self)
        


class Prefix(models.Model):
    prefixgroup = models.ForeignKey(PrefixGroup, related_name='prefix', on_delete=models.CASCADE)
    prefix_boilerplate = models.ForeignKey(Boilerplate, related_name='prefix_boilerplate', on_delete=models.CASCADE)
    prefix = models.CharField(max_length=1000)
    #state value constraint
    system_fun_item = models.CharField(max_length=1000)
    state_or_verb =  models.CharField(max_length=1000)
    #Between Verb - Object
    prefix_quantifier = models.CharField(max_length=1000, blank=True)
    prefix_numerical = models.CharField(blank=True, null=True, max_length=1000) 
    prefix_mumerical_units = models.CharField(max_length=1000, blank=True)
    #occuring functionality
    item_function_flow_statevalue = models.CharField(max_length=1000)
    #state value constraint OR occuring functionality
    logical_expression =  models.CharField(max_length=1000, blank=True)
    #Logic connectivity Prefix1 (and/or/xor) Prefix2
    logic_connective = models.CharField(max_length=1000, blank=True)
    #Prefix value P1/P2/P3
    simple_prefix = models.CharField(max_length=1000, blank=True)


    def __init__(self, *args, **kwargs):
        super(Prefix, self).__init__(*args, **kwargs)
        self.__important_fields = ['logic_connective']
        for field in self.__important_fields:
            setattr(self, '__original_%s' % field, getattr(self, field))


    def save(self, **kwargs):
        """Create P1, P2 or P3 """
        prev_prefix_list = []
        super(Prefix, self).save(**kwargs)
        for field in self.__important_fields:
            orig = '__original_%s' % field
            previous_simple_prefix = getattr(self, orig)
            prev_prefix_list.append(previous_simple_prefix)
        if previous_simple_prefix == "" and not self.logic_connective == "" :
            create_extra_prefix ( self, self.logic_connective)
        

    class Meta:
        ordering = ['prefix_boilerplate']


class BoilerplateData(models.Model):
    owner_data = models.ForeignKey(User, related_name='boilerplate_data', on_delete=models.CASCADE)
    group_of_boilerplate_data = models.ForeignKey(BoilerplateGroup, related_name='group_of_boilerplate_data', on_delete=models.CASCADE)    
    boilerplate_data_id = models.ForeignKey(Boilerplate, related_name='boilerplate_data_id', on_delete=models.CASCADE)
    title_data = models.CharField(default="", max_length=30, unique=True)
    prefix_data = models.CharField(default="", max_length=100, blank=True)
    main_data = models.CharField(default="", max_length=100, blank=True)
    suffix_data =  models.CharField(max_length=100, blank=True)

    class Meta:
        ordering = ['boilerplate_data_id']


class InferenceResults(models.Model):
    owner_infer = models.ForeignKey(User, related_name='owner_infer', on_delete=models.CASCADE)    
    infer_group_of_boilerplate = models.ForeignKey(BoilerplateGroup, related_name='infer_owner_of_boilerplate', on_delete=models.CASCADE)
    ontology_file = models.CharField(max_length=1000, blank=True)
    inference_data = models.CharField(default="", max_length=1000000, blank=True)

    def save(self, **kwargs):
        super(InferenceResults, self).save(**kwargs)
        update_instances(self)
        

    class Meta:
        ordering = ['owner_infer']


class BoilerplateGroupClassesInstances(models.Model):
    classes_instances_group_of_boilerplate = models.ForeignKey(BoilerplateGroup, related_name='classes_instances_owner_of_boilerplate', on_delete=models.CASCADE)
    classes_instances_owner = models.ForeignKey(User, related_name='classes_instances_owner', on_delete=models.CASCADE)
    ontology_file = models.CharField(default="", max_length=1000, blank=True)
    prefix_choices = JSONField()
    prefix_logcon_choices = JSONField()
    prefix_verb_choices = JSONField()
    main_class_choices =JSONField()
    system_choices=JSONField()
    function_choices = JSONField()
    quantifier_choices = JSONField()
    item_choices = JSONField()
    inteface_choices = JSONField()
    connection_choices = JSONField()
    flow_choices = JSONField()
    state_choices =JSONField()
    state_set_choices = JSONField()
    state_value_choices = JSONField()
    main_verb_choices = JSONField()

    numerical_affirmative_choices = JSONField()
    number_unit_choices =JSONField()

    suffix_choices = JSONField()


class BoilerplateGroupDictionary(models.Model):
    classes_instances_group_of_boilerplate_dic = models.ForeignKey(BoilerplateGroup, related_name='classes_instances_owner_of_boilerplate_dic', on_delete=models.CASCADE)
    classes_instances_owner_dic = models.ForeignKey(User, related_name='classes_instances_owner_dic', on_delete=models.CASCADE)
    ontology_file_dic = models.CharField(default="", max_length=1000, blank=True)
    Dictionary_Data = JSONField()
