# Copyright (C) 2015 Ryan Fisher, Gary Hemming
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
import json
import glue.segments as segments


################################################################################
#
#  This module should contain functions that load in the standard JSON formats
#  returned by the dqsegdb, and parse them into expected python objects.  Thus
#  far, the only anticipated function will be to convert several json flag
#  objects into one xml file.  The other JSON parsing for segment computations
#  is trivial, as shown here:
#
# Example from a file:
#[rpfisher@sugar-dev2 ~]$ cat test_jsonnocomments.json
#{
# "meta": {
#    "query_uri" : "uri",
#    "query_time" : 1056789101,
#    "query_start" : 1056788101,
#    "query_end" : 1056789101
#    },
#  "resource_type" : ["resource_uri_1", "resource_uri_2"]
#}
#python:
#>>> blah=open('test_jsonnocomments.json','r')
#>>> a=json.load(blah)
#
################################################################################

def convertJSONtoXML(json_response):
    """
    Incomplete!!
    Converts a standard JSON response from the dqsegdb server into a
    DQXML format.

    """
    # Assumes JSON input as defined in API doc (dictionary containing keys
    # "meta" and "flags")
    data=json.loads(json_response)
    metadata=data['meta']
    flag_list=data['flags']
    flags=[]
    for flag in flag_list:
        ifo=flag['ifo']
        name=flag['name']
        version=flag['version']
        comment=flag['comment']
        provenance_url=flag['provenance_url']
        deactivated=flag['deactivated']
        active_indicates_ifo_badness=flag['active_indicates_ifo_badness']
        known_segments=convert_json_list_to_segmentlist(flag['known'])
        active_segments=convert_json_list_to_segmentlist(flag['active'])
        flags.append(stuff)  ### Fix!!! : Need to make this a class with elements probably to keep things organized.
        ### Alternatively, I could just keep the dictionary and at least replace the json list segments with segmentlist objects?
        ### What's the use case?  I need to put the information about this flag into the DQXML file:  So, I should look at what the old tools did with the information before writing it out to the file/screen!


    ### Fix!!! Incomplete

################################################################################
#
#  Helper function to build a flag_version dictionary object for JSON output
#
################################################################################

def buildFlagDict(ifo,name,version,known_segments,active_segments):
    """
    Helper function to build a flag_version dictionary for JSON production.

    known_segments and active_segments are assumed to be segmentlist objects
    Note that this currently does not generate a false "query_metadata" block
    """
    flag={}
    flag['ifo']=ifo
    flag['name']=name
    flag['version']=version
    flag['known']=convert_segmentlist_to_json(known_segments)
    flag['active']=convert_segmentlist_to_json(active_segments)
    return flag



class FlagVersion(object):
    """
    Class to set up a flag version object for parsing into JSON
    """
    def __init__(self,ifo,name,version):
        self.known=segments.segmentlist([])
        self.active=segments.segmentlist([])
#        self.metadata={}
        self.flagDict={}
        self.ifo=ifo
        self.name=name
        self.version=version
    def buildURL(self,server=''):
        url=server+'/dq/'+'/'.join([str(self.ifo),str(self.name),str(self.version)])
        return url
    def appendKnown(self,known_segments):
        """ Appends a segmentlist of known segments to the existing known
        segments for the object, and coalesces.
        """
        # known_segments must be a segmentlist object
        self.known=self.known+known_segments
        self.known.coalesce()
    def appendActive(self,active_segments):
        """ Appends a segmentlist of active segments to the existing active
        segments for the object, and coalesces.
        """
        # active_segments must be a segmentlist object
        self.active=self.active+active_segments
        self.active.coalesce()

    def buildFlagDictFromVersion(self):
        self.flagDict=buildFlagDict(self.ifo,self.name,self.version,self.known,self.active)

class PatchFlagVersion(FlagVersion):
    __doc__ = FlagVersion.__doc__ + """
    Class to extend basic flag version class to include insert_history
    """
    def __init__(self,ifo,name,version,hackDec11=False):
        self.known=segments.segmentlist([])
        self.active=segments.segmentlist([])
#        self.metadata={}
        self.flagDict={}
        self.ifo=ifo
        self.name=name
        self.version=version
        self.temp_process_ids={} # Used to hold the data
        #                         # associated with a process_id
        if hackDec11:
            self.insert_history={}
        else:
            self.insert_history=[] # holds the process_metadatas and insertion_metadatas    # Note that this assumes that proper dictionaries are appended to this list
        #self.process_metadata={}
        # self.process_metadata.keys() should be process_start_timestamp(or process_start_time for old server hack for Dec 11),uid,args,pid,fqdn,name
        #self.insertion_metadata={}
        # self.insertion_metadata.keys() should be insert_data_stop,insert_data_start,uri,timestamp,auth_user,insert_data_start
    def buildFlagDictFromPatchVersion(self):
        self.buildFlagDictFromVersion()
        self.flagDict['insert_history']=self.insert_history
    def coalesceInsertHistory(self):
        final_history=[]
        first=True
        debug=False
        if debug:
            print("Printing insert history")
            print(self.insert_history)
            print("length:")
            print(len(self.insert_history))

        for i in self.insert_history:
            if debug:
                print("self.insert_history element:")
                print(i)
            if first:
                final_history.append(i)
                first = False
                if debug:
                    print("first!")
            else:
                process_name=i['process_metadata']['name']
                process_pid=i['process_metadata']['pid']
                process_uid=i['process_metadata']['uid']
                matched=False
                for j in final_history:
                    if debug:
                        print("printing i")
                        print(i)
                        print("printing j for comparison")
                        print(j)
                    if process_name==j['process_metadata']['name'] and process_pid==j['process_metadata']['pid'] and process_uid==j['process_metadata']['uid']:  # FIX!!! Should we sort these by time order and then match end time of file_history element to start_time of insert_history element?
                        j['insertion_metadata']['insert_data_stop']=max(j['insertion_metadata']['insert_data_stop'],i['insertion_metadata']['insert_data_stop'])
                        j['insertion_metadata']['insert_data_start']=min(j['insertion_metadata']['insert_data_start'],i['insertion_metadata']['insert_data_start'])
                        if debug:
                            print("i matched j")
                            matched=True
                    else:
                        if debug:
                            print("i didn't match j")
                if not matched:
                    final_history.append(i)
        if debug:
            print("Printing final history:")
            print(final_history)
            print(len(final_history))
        self.insert_history=final_history


class InsertFlagVersion(PatchFlagVersion):
    __doc__ = PatchFlagVersion.__doc__ + """
    Adds metadata for initial inserts
    """
    def __init__(self,ifo,name,version):
        super(InsertFlagVersion, self).__init__(ifo,name,version)
        self.metadata={}
        self.flag_version_comment=""
        self.flag_description=""
        self.provenance_url=""
        self.deactivated=False
        self.active_indicates_ifo_badness=True
    def buildFlagDictFromInsertVersion(self):
        ### Fix!!! I think I should make this a function that takes the self.x arguments
        ### as inputs and returns a modified flagDict object, so I can use it other
        ### places
        self.buildFlagDictFromPatchVersion()
        self.flagDict['metadata']={}
        self.flagDict['metadata']['flag_description']=self.flag_description
        self.flagDict['metadata']['flag_version_comment']=self.flag_version_comment
        self.flagDict['metadata']['further_info_url']=self.provenance_url
        self.flagDict['metadata']['deactivated']=self.deactivated
        self.flagDict['metadata']['active_indicates_ifo_badness']=self.active_indicates_ifo_badness

class InsertFlagVersionOld(PatchFlagVersion):
    __doc__ = PatchFlagVersion.__doc__ + """
    Adds metadata for initial inserts
    """
    def __init__(self,ifo,name,version):
        super(InsertFlagVersionOld, self).__init__(ifo,name,version)
        self.metadata={}
        self.version_comment=""
        self.flag_comment=""
        self.provenance_url=""
        self.deactivated=False
        self.active_indicates_ifo_badness=True
    def buildFlagDictFromInsertVersion(self):
        ### Fix!!! I think I should make this a function that takes the self.x arguments
        ### as inputs and returns a modified flagDict object, so I can use it other
        ### places
        self.buildFlagDictFromPatchVersion()
        self.flagDict['metadata']={}
        self.flagDict['metadata']['flag_comment']=self.flag_comment
        self.flagDict['metadata']['version_comment']=self.version_comment
        self.flagDict['metadata']['provenance_url']=self.provenance_url
        self.flagDict['metadata']['deactivated']=self.deactivated
        self.flagDict['metadata']['active_indicates_ifo_badness']=self.active_indicates_ifo_badness


################################################################################
#
#  Helper functions to convert segmentlist to json list of lists type object
#  and vice versa
#
################################################################################

def convert_segmentlist_to_json(segmentlist_input):
    """
    Helper function used to convert segmentlist to json list of lists type
    object.
    """
    json_list=[[x[0],x[1]] for x in segmentlist_input]
    return json_list

def convert_json_list_to_segmentlist(jsonlist):
     """
     Helper function used to convert json list of lists type object to a
     segmentlist object
     """
     segment_list=segments.segmentlist([segments.segment(x[0],x[1]) for x in jsonlist])
     return segment_list

################################################################################
#
#  Conversions from JSON to user requested formats
#
################################################################################

def generated_vdb_ascii(json_str,filepath):
    res_dict=json.loads(json_str)
    active_list=res_dict['active']
    active_segments=segments.segmentlist([segments.segment(x[0],x[1]) for x in active_list])
    known_list=res_dict['known']
    known_segments=segments.segmentlist([segments.segment(x[0],x[1]) for x in known_list])
    query_start=res_dict['query_information']['start']
    query_stop=res_dict['query_information']['end']
    if query_start!=0 and query_stop!=0:
        requested_span=segments.segmentlist([segments.segment(query_start,query_stop)])
    else:
        requested_span=segments.segmentlist([segments.segment(0,9999999999)])
    active_segments_string=',1 \n'.join([str(i[0])+","+str(i[1]) for i in active_segments])+",1 \n"
    unknown_segments=requested_span-known_segments
    unknown_segments_string=',-1 \n'.join([str(i[0])+","+str(i[1]) for i in unknown_segments])+",-1 \n"
    known_not_active_segments=known_segments-active_segments
    known_not_active_segments_string=',0 \n'.join([str(i[0])+","+str(i[1]) for i in known_not_active_segments])+",0 \n"
    output_fileh=open(filepath,'w+')
    query_info_string=json.dumps(res_dict['query_information'], indent=1)
    output_fileh.writelines(query_info_string)
    output_fileh.write('\n')
    output_fileh.writelines(active_segments_string)
    output_fileh.writelines(unknown_segments_string)
    output_fileh.writelines(known_not_active_segments_string)
    output_fileh.close()
    return filepath

def generated_ascii(json_str,filepath):
    res_dict=json.loads(json_str)
    active_list=res_dict['active']
    active_segments=segments.segmentlist([segments.segment(x[0],x[1]) for x in active_list])
    active_segments_string='\n'.join([str(i[0])+","+str(i[1]) for i in active_segments])
    output_fileh=open(filepath,'w+')
    output_fileh.writelines(active_segments_string)
    output_fileh.close()
    return filepath


################################################################################
#
#  Parse basic list results json objects:
#  Example from API Document:
#  {
#   "meta": {
#      "query_uri" : "uri",
#      "query_time" : 1056789101,
#      "query_start" : 1056788101,
#      "query_end" : 1056789101
#      },
#    "resource_type" : ["resource_uri_1", "resource_uri_2"]
#  }
#
#  Fix!!! NOTE: I just realized that the above format isn't very programmatic:  you
#  can't look for a key that will tell you what you are looking at, e.g. the
#  resource_type gets replaced by "version", but there is no key with a value
#  of "version".  Having that last part would make for cleaner parsing of
#  results.
#
################################################################################

################################################################################
#
# Basic Insert Example:
# {
#   "insert_process" : {
#     "insert_uri" : "uri",   // contains the URI specified with the PUT HTTP method
#     "process_id": pid, // process id number
#     "process_full_name": "process name",
#     "process_fqdn": "fully qualified domain name",
#     "process_args": ["arg1","arg2"],
#     "process_user" : "user" // user who ran the insert process on the client
#     "inserted_data_start": gpstime, // First gpstime of data this insertion describes
#     "inserted_data_end": gpstime, // Last gpstime of data this insertion describes
#     "process_start_timestamp": gpstime, // When the insert process was started, use to determine run time of inserts
#     // NOT to be added by client code -- these are server-side annotations, prior to insertion into the DB
#     "auth_user" : "user identification" // from the auth infrastructure used to talk to the server
#     "insert_timestamp" : gpstime,    // when the insert was committed to the DB
#    },
#    "flag" : {
#         "ifo" : "ifo",
#         "name" : "flag",
#         "comment" : "description",
#         "URL" : "aLog URL",
#         "deactivated" : false|true,
#         "active_indicates_ifo_badness" : true|false|null,
#         // all of the above will be needed for /dq/IFO/FLAG inserts
#         "version" : n,
#         // all of the above will be needed for /dq/IFO/FLAG/VERSION inserts
#         // all of the below are required for /dq/IFO/FLAG/VERSION/active inserts
#         "known" : [ [ts,te], [ts,te], ... ]
#         // Note that active segments do not actually have to be included, if the flag was known to be inactive:
#         "active" : [ [ts,te], [ts,te], ... ]
#         // \textcolor{red}{Comment: or "segment" : [ [ts,te,value], [ts,te,value], ...] (where value can be -1,0 or +1)}
#         // inactive == (known - active)
#         // unknown == (all_time - known)
#    }
# }
#
################################################################################

