#!/usr/bin/env python
# -----------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
# -----------------------------------------------------------------------

import os
import re
import sys
import time
import getopt

from ducc_util import DuccUtil                        # sets the sys.path so we can find stuff
from properties import Properties
from properties import Property

class DuccPropManager:
    def __init__(self):
        # simple bootstrap to establish DUCC_HOME and to set the python path so it can
        # find the common code in DUCC_HOME/admin
        # Infer DUCC_HOME from our location - no longer use a (possibly inaccurate) environment variable
        me = os.path.abspath(__file__)    
        ndx = me.rindex('/')
        ndx = me.rindex('/', 0, ndx)
        self.DUCC_HOME = me[:ndx]          # split from 0 to ndx

    def merge(self, file1, file2, file3):
        '''
            Merge "file1" and "file2" to produce a merged "file3".
        '''
        file1_props = Properties()
        file1_props.load(file1)

        file2_props = Properties()
        file2_props.load(file2)

        file3_props = Properties()

        # Check for a site specification override of the property renamed in 2.2.3
        oname = 'ducc.database.host'
        nname = 'ducc.database.host.list'
        prop = file2_props.get_property(oname)
        if prop != None:
            prop.c.append('# RENAMED by merger ' + time.strftime('%c') + ' old name: ' + prop.k)
            prop.k = nname
            file2_props.put_property(prop)
            file2_props.delete(oname)
            
        # Check for a site specification override of the property renamed in 2.1.0
        oname = 'ducc.threads.limit'
        nname = 'ducc.job.max.pipelines.count'
        prop = file2_props.get_property(oname)
        if prop != None:
            prop.c.append('# RENAMED by merger ' + time.strftime('%c') + ' old name: ' + prop.k)
            prop.k = nname
            file2_props.put_property(prop)
            file2_props.delete(oname)

        # first pass, create merged props with stuff in base props file updated with delta
        for k in file1_props.get_keys():
            base = file1_props.get_property(k)
            upd = file2_props.get_property(k)
            if ( upd == None ):
                file3_props.put_property(base)
            else:
                upd.c.append('# OVERRIDE by merger ' + time.strftime('%c') + ' old value: ' + base.v)
                file3_props.put_property(upd)
                file2_props.delete(k)

        # everything left in delta is new stuff
        for k in file2_props.get_keys():
            upd = file2_props.get_property(k)
            upd.c.append('# INSERT by merger ' + time.strftime('%c'))
            file3_props.put_property(upd)

		# automagically fix // in path to java
        prop = file3_props.get_property('ducc.jvm')
        prop.v = prop.v.replace('//', '/')
        file3_props.put_property(prop)
		
        file3_props.write(file3)

    def delta(self, left, right, delta):
        '''
            Compare "left" to "right" to produce a "delta".
        '''

        left_props = Properties()
        left_props.load(left)

        right_props = Properties()
        right_props.load(right)

        delta_props = Properties()

        for k in left_props.get_keys():
            r = left_props.get_property(k)
            if ( r == None ):          
                continue                                # left only, 'master' no need to delta it

            l = left_props.get_property(k)
            r = right_props.get_property(k)
            if ( r != None and l.v != r.v ):
                delta_props.put_property(r)            # no match, save difference (right) in delta

            right_props.delete(k)                        # shrink left to see what's left over

        for k in right_props.get_keys():
            delta_props.put_property(right_props.get_property(k))
            
        delta_props.write(delta)

    def usage(self, *msg):
        if ( msg[0] != None ):
            print ''
            print ' '.join(msg)
            print ''

        print 'ducc_prop_manager has two functions:'
        print '    1. Merge a local properties file with a default file to produce a merged ducc.properties.'
        print '    2. Compare two properties files to create a local delta.'
        print ''
        print '    The merge and delta operations are inverse operations.'
        print ''
        print '    Comments and the structure of the default file are preserved whenever possible.'
        print ''
        print '    If the full path name to a file is not given, it is must reside within'
        print'        $DUCC_HOME/resources'
        print ''
        print "Usage:"
        print '   ducc_prop_manager --merge file1 --with file2 --to file3'
        print '   ducc_prop_manager --delta file1 --with file2 --to file3'
        print ''
        print 'Options'
        print '    -m, --merge file1'
        print '    -d, --delta file1'
        print '           This is the base properties file, usually the unmodified file provided with the'
        print '           DUCC distribution.'
        print ''
        print '           If --merge is specified, the output file (file3) is the merger of file1 and file2'
        print ''
        print '           If --delta is specified, the output file (file3) is the delta of file1 and file2'
        print ''
        print '    -w, --with file2'
        print '          This file is either merged or difference with file1 to produce the result in file3'
        print ''
        print '    -t, --to file3'
        print '          This is the result of the merge or delta operation'
        print ''
        print 'Examples:'
        print '    Update your ducc.properties from the default properties and your site.ducc.properties:'
        print '        ducc_props_manager --merge default.ducc.properties --with site.ducc.properties --to ducc.properties'
        print ''
        print '    Create a new site.ducc.properties by differencing the default properties and ducc.properties'
        print '        ducc_props_manager --delta default.ducc.properties --with ducc.properties --to site.ducc.properties'

        sys.exit(1);

    def main(self, argv):          
        action = None
        file1 = None
        file2 = None
        file3 = None
        try:
            opts, args = getopt.getopt(argv, 'm:w:t:h?', ['delta=', 'merge=', 'with=', 'to=', 'help'])
        except:
            self.usage('Invalid arguments', ' '.join(argv))
                  
        for ( o, a ) in opts:
            if o in ( '-m', '--merge' ): 
                action = 'merge'
                file1 = a
            elif o in ( '-d', '--delta' ):
                action = 'delta'
                file1 = a
            elif o in ( '-w', '--with' ):
                file2 = a
            elif o in ( '-t', '--to' ):
                file3 = a

            elif o in ('-h', '-?', '--help'):
                self.usage(None);

        if ( action == None or file1 == None or file2 == None or file3 == None ):
            self.usage("Insufficient arguemnts.  All arguments are required.")

        if ( not ( file1.startswith('/') or file1.startswith('.') ) ):
            file1 = '/'.join([self.DUCC_HOME, 'resources', file1])

        if ( not ( file2.startswith('/') or file2.startswith('.') ) ):
            file2 = '/'.join([self.DUCC_HOME, 'resources', file2])

        if ( not ( file3.startswith('/') or file3.startswith('.') ) ):
            file3 = '/'.join([self.DUCC_HOME, 'resources', file3])

        if ( action == 'merge' ):
            self.merge(file1, file2, file3)
        elif ( action == 'delta'):
            self.delta(file1, file2, file3)
        else:
            self.usage('Invalid action:', action, 'must be --delta or --merge')

if __name__ == "__main__":
    mgr = DuccPropManager()
    mgr.main(sys.argv[1:])
