# -*- coding: utf-8 -*-
#    A validation script for Bonnmotion (http://net.cs.uni-bonn.de/wg/cs/applications/bonnmotion/)
#    Copyright (C) 2011 University of Bonn
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU 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 General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

from Config import Config
from Common import InputDirOrFile

import os
import string
from copy import copy

DELIMITERSETS = ';'
DELIMITERSTEPS = ':'

class BonnmotionParamsGenerator(object):
    def setApptestFilename(self, apptestFilename):
        self.apptestFilename = apptestFilename

    def setModeltestFilename(self, modeltestFilename):
        self.modeltestFilename = modeltestFilename
    
    def createBonnmotionParamsFiles(self):
        testcases = self._readInputFile()
        self._createParamFilesforBM(testcases)

    ##
    #input: string (e.g. "1:9:2")
    #output: array of steps. (e.g. [1, 3, 5, 7, 9])
    def _calcVariableSteps(self, variablestr):
        tmp = variablestr.split(DELIMITERSTEPS)
        for x in tmp: x.strip()
        begin = float(tmp[0]) if '.' in tmp[0] else int(tmp[0])
        end = float(tmp[1]) if '.' in tmp[1] else int(tmp[1])
        step = float(tmp[2]) if '.' in tmp[2] else int(tmp[2])

        S = []
        y = begin
        while y <= end:
            S.append(y)
            y += step

        return S
    
    ##
    #input: string (e.g. "1;2;3")
    #output: array of the set. (e.g. [1, 2, 3])   
    def _calcVariableSets(self, variablestr):
        tmp = variablestr.split(DELIMITERSETS)
        for x in tmp: x.strip()
        
        return tmp
    
    ##
    #input: 
    #        modeltestFilename: string with modeltestFilename
    #        constantparams: empty dictionary to be filled with constant parameters
    #        variableparams: empty dictionary to be filled with variable parameters
    #
    def _readInputFile(self):
        inputfiledict = {}
        testcase = 0 ##default testcase
        testcasearray = []

        f = open(self.modeltestFilename)

        for line in f:
            if "[testcase]" in line.lower():
                testcasearray.append(inputfiledict.copy())
                inputfiledict.clear()
            elif "=" in line:
                key, inputfiledict[key] = (s.strip() for s in line.split("="))             
        
        testcasearray.append(inputfiledict.copy())        
        f.close()

        self.modelname = testcasearray[0]['model']
         
        result = []     
        constantparams = {}
        variableparams = {}          
        
        for y in testcasearray:
            for x in testcasearray[testcase]:  
                if "{" in testcasearray[testcase][x]:
                    if DELIMITERSTEPS in testcasearray[testcase][x]:                     #parameter is variable
                        variableparams[x] = self._calcVariableSteps(testcasearray[testcase][x][1:-1])
                    elif DELIMITERSETS in testcasearray[testcase][x]:                   #parameter is a set
                        variableparams[x] = self._calcVariableSets(testcasearray[testcase][x][1:-1])
                    elif '$' in testcasearray[testcase][x]:
                        constantparams[x] = os.path.join(os.getcwd(), os.path.dirname(self.modeltestFilename), testcasearray[testcase][x][3:])
                    else:
                        variableparams[x] = [testcasearray[testcase][x][1:-1]]
                else:                                               #parameter is constant
                    constantparams[x] = testcasearray[testcase][x] 
            
            result.append((constantparams.copy(), variableparams.copy()))
            testcase += 1 
            constantparams = result[0][0].copy()  ##default case
            variableparams = result[0][1].copy()  ##default case        
        return result
                
    ## creates all parameter combinations and safes them to .params files
    #input: 
    #        constantparams: dictionary of constant parameters
    #        variableparams: dictionary of variable parameters
    #output: 
    #        n: number of outputfiles created
    def _createParamFilesforBM(self, testcases):
        n = 0
        
        for testcase in testcases:
            (constantparams, variableparams) = testcase
            
            for x in variableparams: 
                constantparams[x] = str(variableparams[x][0])
            
            if (len(variableparams) == 0):                      # if there are no variable params there is only one constant
                self._writeParametersToFile(constantparams, Config().readConfigEntry('tempoutputparamsfile').replace('INDEX', '0'))
                self.noOfFilesCreated = 1  
                return        
            
            first = 0                                           # avoids redundant combinations
            for x in variableparams:
                output = constantparams.copy()                  # copy constant parameters to output
                
                for y in variableparams[x][first:]:
                    output[x] = str(y)
                    self._writeParametersToFile(output, Config().readConfigEntry('tempoutputparamsfile').replace('INDEX', str(n))) 
                    n += 1
                first = 1
        self.noOfFilesCreated = n
    
    ##
    #input: 
    #        params: dictionary of parameters
    #        modeltestFilename: string with modeltestFilename 
    def _writeParametersToFile(self, params, filename):
        f = open(filename, 'w')
        for x in params:
            f.write(x + '=' + params[x] + '\n')
        f.close()
 
    ##
    #    result: array of dictionaries of parameters (representing testcases)
    def parseApptestFile(self):
        testcase = 0
        result = {}       
        result['cases'] = []
        result['cases'].append({})
        f = open(self.apptestFilename, 'r')
        
        for line in f:
            if '=' in line:
                key, value = (s.strip() for s in line.split("="))
                if key == 'app':
                    result['app'] = value
                elif key == 'paramsfile':
                    result['paramsfile'] = value
                    if os.path.isfile(result['paramsfile']):
                        result['inputDirOrFile'] = InputDirOrFile.FILE
                    elif os.path.isdir(result['paramsfile']):
                        result['inputDirOrFile'] = InputDirOrFile.DIRECTORY
                elif key == 'extensions':
                    result['cases'][testcase]['extensions'] = self._calcVariableSets(value[1:-1])
                elif key == 'appparams':
                    if '$' in value:
                        value = string.replace(value, '$', os.path.join(os.getcwd(), os.path.dirname(self.apptestFilename)) + os.sep)
                    result['cases'][testcase][key] = value
                else:
                    raise Exception("invalid key provided in apptest file: " + line)
            elif "[testcase]" in line.lower():
                testcase += 1
                result['cases'].append({})
                if testcase > 1:
                    if 'extensions' not in result['cases'][testcase-1]:
                        result['cases'][testcase-1]['extensions'] = result['cases'][0]['extensions']

        if testcase > 0:
            if 'extensions' not in result['cases'][testcase]:
                result['cases'][testcase]['extensions'] = result['cases'][0]['extensions']
        f.close()

        return result