#!/usr/bin/env python
# -*- coding: utf-8 -*-

# -------------------------------------------------------------------
# Copyright (c) 2010-2019 Denis Machard
# This file is part of the extensive automation project
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA
# -------------------------------------------------------------------

import time
import datetime
import pickle
import subprocess
import threading
import os
import parser
try:
    import ConfigParser
except ImportError:  # python3 support
    import configparser as ConfigParser
import uuid
import sys
import zlib
import signal
import base64
import shutil
import copy
import hashlib
import json
import platform
import traceback
try:
    import cStringIO
except ImportError:  # support python 3
    import io as cStringIO
try:
    xrange
except NameError:  # support python3
    xrange = range

from ea.serverengine import DbManager
from ea.serverengine import StorageDataAdapters
from ea.serverengine import UsersManager
from ea.serverengine import ProjectsManager
from ea.serverengine import AgentsManager
from ea.libs import Scheduler, Settings, Logger
from ea.libs.FileModels import TestResult as TestResult
from ea.serverinterfaces import EventServerInterface as ESI
from ea.serverinterfaces import TestServerInterface as TSI
from ea.serverrepositories import (RepoArchives,
                                   RepoManager)
from ea.testcreatorlib import (TestModel, TestModelDesign,
                               TestModelCommon, TestModelSub)

TASKS_RUNNING = 0
TASKS_WAITING = 1
TASKS_HISTORY = 2

STATE_INIT = 'INIT'
STATE_WAITING = 'WAITING'
STATE_DISABLED = 'DISABLED'
STATE_RUNNING = 'RUNNING'
STATE_COMPLETE = 'COMPLETE'
STATE_ERROR = 'ERROR'
STATE_FINISHED = 'FINISHED'

STATE_KILLING = 'KILLING'
STATE_KILLED = 'KILLED'

STATE_CANCELLING = 'CANCELLING'
STATE_CANCELLED = 'CANCELLED'

STATE_UPDATING = 'UPDATING'

SIGNAL_KILL = 9

SCHED_TYPE_UNDEFINED = -1
SCHED_EVERY_X = 7
SCHED_WEEKLY = 8
SCHED_NOW_MORE = 9
SCHED_QUEUE = 10
SCHED_QUEUE_AT = 11

SCHEDULE_TYPES = {
    "daily": 6,
    "hourly": 5,
    "weekly": 8,
    "every": 7,
    "at": 1,
    "in": 2,
    "now": 0
}

MONDAY = 0
TUESDAY = 1
WEDNESDAY = 2
THURSDAY = 3
FRIDAY = 4
SATURDAY = 5
SUNDAY = 6

EMPTY_VALUE = ''

GenBaseId = 0
GenBaseIdMutex = threading.RLock()

def getGroupId():
    """
    Generates a new unique ID.
    """
    global GenBaseId
    GenBaseIdMutex.acquire()
    GenBaseId += 1
    ret = GenBaseId
    GenBaseIdMutex.release()
    return ret

def getBackTrace():
    """
    Returns the current backtrace.
    """
    backtrace = cStringIO.StringIO()
    traceback.print_exc(None, backtrace)
    ret = backtrace.getvalue()
    backtrace.close()
    return ret

class Task(Logger.ClassLogger):
    def __init__(self, testData, testName, testPath, testUser, testId,
                 testBackground, taskEnabled=True, withoutProbes=False,
                 debugActivated=False, withoutNotif=False, noKeepTr=False,
                 testUserId=0, testProjectId=0, stepByStep=False,
                 breakpoint=False, runSimultaneous=False, channelId=False,
                 context=None):
        """
        Construc test class
        """
        self.ctx = context

        self.mutex = threading.RLock()

        self.channelId = channelId
        self.schedId = None
        self.testName = testName
        self.testPath = testPath

        self.userName = testUser
        self.userId = testUserId
        self.projectId = testProjectId

        self.dataTest = testData
        self.testId = testId
        self.background = testBackground
        self.verdict = 'UNDEFINED'
        self.enabled = taskEnabled
        self.withoutProbes = withoutProbes
        self.noKeepTr = noKeepTr
        self.debugActivated = debugActivated
        self.withoutNotif = withoutNotif
        self.stepByStep = stepByStep
        self.breakpoint = breakpoint
        self.runSimultaneous = runSimultaneous

        self.testType = 'TestSuite'
        if self.dataTest['test-extension'] == 'tgx':
            self.testType = 'TestGlobal'
        if self.dataTest['test-extension'] == 'tpx':
            self.testType = 'TestPlan'
        if self.dataTest['test-extension'] == 'tux':
            self.testType = 'TestUnit'

        self.replayId = 0
        self.recurId = 0

        self.schedAt = 0
        self.schedType = SCHED_TYPE_UNDEFINED
        self.schedArgs = (0, 0, 0, 0, 0, 0)  # Y,M,D,H,M,S
        self.schedFrom = (0, 0, 0, 0, 0, 0)  # Y,M,D,H,M,S
        self.schedTo = (0, 0, 0, 0, 0, 0)  # Y,M,D,H,M,S
        self.schedNb = -1

        self.resultsStats = {"passed": 0, "failed": 0, "undefined": 0}

        self.prepareTime = 0
        self.prepared = False
        self.syntaxOK = False
        self.startTime = "N/A"
        self.stopTime = "N/A"
        self.duration = "N/A"
        self.lastError = None

        self.tpid = None
        self.eventReg = None
        self.removeFromContext = True

        self.groupId = 0

        # Universally Unique IDentifiers
        self.taskUuid = "%s" % uuid.uuid4()
        self.state = self.setState(STATE_INIT)

    def saveTestDescr(self, envTmp=False):
        """
        """
        maxRun = "unlimited"
        if self.schedType == Scheduler.SCHED_NOW:
            maxRun = 1
        else:
            if self.schedNb != -1:
                maxRun = self.schedNb

        test = {"name": self.testName, "start-at": self.startTime}
        statistics = {"total": "%s" % maxRun, 'count': "%s" % (self.recurId + 1),
                      'passed': "%s" % self.resultsStats["passed"],
                      'failed': "%s" % self.resultsStats["failed"],
                      'undefined': "%s" % self.resultsStats["undefined"]}
        testDescr = {
            "test": test,
            "user": self.userName,
            "statistics": statistics}
        f = open("%s/DESCRIPTION" % self.getPath(envTmp=envTmp), 'w')
        f.write("%s" % json.dumps(testDescr))
        f.close()

    def setGroupId(self, groupId):
        """
        Set the id of tasks group in queue
        """
        self.groupId = groupId

    def setSyntaxOK(self):
        """
        set syntax as ok
        """
        self.trace("Syntax OK")
        self.syntaxOK = True
        return self.syntaxOK

    def runAgain(self):
        """
        Check the counter of run
        """
        if self.schedNb == -1:
            return True
        elif self.recurId < (self.schedNb - 1):
            return True
        else:
            return False

    def alwaysRun(self):
        """
        Check if always in the interval (from/to)
        """
        if self.schedType == SCHED_EVERY_X:
            if self.schedTo == self.schedFrom:
                return True
            else:

                cur_dt = time.localtime()
                now_dt = datetime.datetime(cur_dt.tm_year,
                                           cur_dt.tm_mon,
                                           cur_dt.tm_mday,
                                           cur_dt.tm_hour,
                                           cur_dt.tm_min,
                                           0,
                                           0)

                toY, toM, toD, toH, toMn, _ = self.schedTo
                to_dt = datetime.datetime(toY,
                                          toM,
                                          toD,
                                          toH,
                                          toMn,
                                          0,
                                          0)

                if now_dt >= to_dt:
                    _, _, _, h, mn, s = self.schedArgs
                    fromY, fromM, fromD, fromH, fromMn, _ = self.schedFrom

                    # drift the interval on one day
                    from_dt = datetime.datetime(fromY, fromM, fromD, fromH, fromMn, 0, 0) + \
                        datetime.timedelta(hours=24, minutes=0, seconds=0)
                    to_dt = to_dt + \
                        datetime.timedelta(hours=24, minutes=0, seconds=0)

                    next_dt = from_dt
                    self.schedAt = time.mktime(
                        next_dt.timetuple()) - 60 * 60 * h - 60 * mn - s
                    self.schedFrom = (from_dt.year, from_dt.month,
                                      from_dt.day, from_dt.hour,
                                      from_dt.minute, 0)
                    self.schedTo = (to_dt.year, to_dt.month,
                                    to_dt.day, to_dt.hour,
                                    to_dt.minute, 0)

                    self.trace('Next from: %s' % str(self.schedFrom))
                    self.trace('Next to: %s' % str(self.schedTo))
                    return False
                else:
                    return True
        else:
            return True

    def setEventReg(self, event):
        """
        Save the event created by the scheduler
        """
        self.eventReg = event

    def toTuple(self, withId=False, withGroupId=False):
        """
        Return the task as tuple
        """
        if withId:
            if withGroupId:
                return (self.schedId, self.schedType, str(self.schedArgs), str(self.schedAt),
                        self.getTaskName(), str(self.userName),
                        str(self.startTime), str(self.duration), str(
                            self.state), self.schedNb,
                        self.recurId, self.enabled, self.withoutProbes,
                        self.withoutNotif, self.noKeepTr, self.userId, self.projectId,
                        str(self.schedFrom), str(self.schedTo), self.groupId)
            else:
                # Called to add in backup
                return (self.schedId, self.schedType, str(self.schedArgs), str(self.schedAt),
                        self.getTaskName(), str(self.userName),
                        str(self.startTime), str(self.duration), str(
                            self.state), self.schedNb,
                        self.recurId, self.enabled, self.withoutProbes,
                        self.withoutNotif, self.noKeepTr, self.userId, self.projectId,
                        str(self.schedFrom), str(self.schedTo))
        else:
            # Called to add in the history
            return (self.schedType, str(self.schedArgs), str(self.schedAt), self.getTaskName(),
                    str(self.userName), str(
                        self.startTime), str(self.duration),
                    str(self.state), self.projectId)

    def getId(self):
        """
        Get the scheduler event id
        """
        return self.schedId

    def setId(self, taskId):
        """
        Set the scheduler event id
        """
        self.schedId = taskId

    def setPrepared(self):
        """
        """
        self.prepared = True

    def getSchedTime(self):
        """
        Get the schedulation time
        """
        return self.schedAt

    def getTaskName(self):
        """
        Get the task name
        """
        ret = ''
        if self.testPath != "":
            ret = self.testPath
        else:
            ret = self.testName
        return ret

    def setReplayId(self):
        """
        Set the replay id
        """
        self.removeFinishedFile()
        self.replayId += 1

    def initialize(self, runAt, runType, runNb, runEnabled, withoutProbes,
                   debugActivated, withoutNotif, noKeepTr,
                   runFrom=(0, 0, 0, 0, 0, 0),
                   runTo=(0, 0, 0, 0, 0, 0)):
        """
        Initialize the task start time
        """
        runAt = tuple(runAt)
        self.enabled = runEnabled
        self.withoutProbes = withoutProbes
        self.debugActivated = debugActivated
        self.withoutNotif = withoutNotif
        self.noKeepTr = noKeepTr
        timestamp = 0
        y, m, d, h, mn, s = runAt
        fromY, fromM, fromD, fromH, fromMn, fromS = runFrom
        toY, toM, toD, toH, toMn, toS = runTo
        self.schedNb = int(runNb)
        self.schedType = runType
        self.schedArgs = (0, 0, 0, 0, 0, 0)
        self.schedFrom = runFrom
        self.schedTo = runTo

        if runType == Scheduler.SCHED_DAILY:
            cur_dt = time.localtime()
            next_dt = datetime.datetime(cur_dt.tm_year, cur_dt.tm_mon,
                                        cur_dt.tm_mday, h, mn, s, 0)
            timestamp = time.mktime(next_dt.timetuple())
            self.schedAt = timestamp
            self.schedArgs = (0, 0, 0, h, mn, s)

        elif runType == Scheduler.SCHED_HOURLY:
            cur_dt = time.localtime()
            next_dt = datetime.datetime(cur_dt.tm_year, cur_dt.tm_mon, cur_dt.tm_mday,
                                        cur_dt.tm_hour, mn, s, 0)
            timestamp = time.mktime(next_dt.timetuple())
            self.schedAt = timestamp
            self.schedArgs = (0, 0, 0, 0, mn, s)

        elif runType == Scheduler.SCHED_EVERY_MIN:
            cur_dt = time.localtime()
            next_dt = datetime.datetime(cur_dt.tm_year, cur_dt.tm_mon, cur_dt.tm_mday,
                                        cur_dt.tm_hour, cur_dt.tm_min, s, 0)
            timestamp = time.mktime(next_dt.timetuple())
            self.schedAt = timestamp
            self.schedArgs = (0, 0, 0, 0, 0, s)

        elif runType == Scheduler.SCHED_EVERY_SEC:
            self.error('not implemented')

        elif runType == Scheduler.SCHED_AT:
            dt = datetime.datetime(y, m, d, h, mn, s, 0)
            timestamp = time.mktime(dt.timetuple())
            self.schedAt = timestamp
            self.schedArgs = (y, m, d, h, mn, s)

        elif runType == Scheduler.SCHED_IN:
            timestamp = time.time() + s
            self.schedAt = timestamp
            self.schedArgs = (0, 0, 0, 0, 0, s)

        elif runType == Scheduler.SCHED_NOW:
            self.trace('init start time: nothing todo')

        elif runType == SCHED_QUEUE:
            self.trace('init start time in queue: nothing todo')

        elif runType == SCHED_QUEUE_AT:
            dt = datetime.datetime(y, m, d, h, mn, s, 0)
            timestamp = time.mktime(dt.timetuple())
            self.schedAt = timestamp
            self.schedArgs = (y, m, d, h, mn, s)

        # BEGIN new schedulation type in 5.0.0
        elif runType == SCHED_NOW_MORE:
            if sum(runFrom):
                dt = datetime.datetime(
                    fromY, fromM, fromD, fromH, fromMn, fromS, 0)
                timestamp = time.mktime(dt.timetuple())
                self.schedAt = timestamp
            else:
                pass
        # END

        # BEGIN new schedulation type in 3.1.0
        elif runType == SCHED_EVERY_X:
            cur_dt = time.localtime()

            from_dt = datetime.datetime(cur_dt.tm_year, cur_dt.tm_mon,
                                        cur_dt.tm_mday, fromH, fromMn, 0, 0)
            if fromH > toH:  # fix in v12, sched from 23:00 to 07:00 not working
                to_dt = datetime.datetime(cur_dt.tm_year, cur_dt.tm_mon,
                                          cur_dt.tm_mday + 1, toH, toMn, 0, 0)
            else:
                to_dt = datetime.datetime(cur_dt.tm_year, cur_dt.tm_mon,
                                          cur_dt.tm_mday, toH, toMn, 0, 0)

            next_dt = datetime.datetime(cur_dt.tm_year, cur_dt.tm_mon, cur_dt.tm_mday,
                                        cur_dt.tm_hour, cur_dt.tm_min, cur_dt.tm_sec, 0)
            self.trace("Current next time: %s" % next_dt.isoformat())

            if from_dt == to_dt:
                if not Settings.getInt(
                        'TaskManager', 'everyminx-run-immediately'):
                    next_dt += datetime.timedelta(hours=h,
                                                  minutes=mn, seconds=s)
                self.trace(
                    "No interval, new next time: %s" %
                    next_dt.isoformat())
            else:
                self.trace("Ajust next time according to the interval")
                # start greather than current date ?
                if from_dt > next_dt:
                    next_dt = from_dt
                # shit the interval of one day
                else:
                    from_dt += datetime.timedelta(hours=24,
                                                  minutes=0, seconds=0)
                    to_dt += datetime.timedelta(hours=24, minutes=0, seconds=0)
                    next_dt = from_dt

            self.trace("Final next time at: %s" % next_dt.isoformat())
            timestamp = time.mktime(next_dt.timetuple())
            self.schedAt = timestamp
            self.schedArgs = (0, 0, 0, h, mn, s)
            self.schedFrom = (from_dt.year, from_dt.month,
                              from_dt.day, from_dt.hour,
                              from_dt.minute, 0)
            self.schedTo = (to_dt.year, to_dt.month,
                            to_dt.day, to_dt.hour,
                            to_dt.minute, 0)

            self.trace("Interval begin at: %s" % str(self.schedFrom))
            self.trace("Interval ending at: %s" % str(self.schedTo))

        elif runType == SCHED_WEEKLY:
            cur_dt = time.localtime()
            next_dt = datetime.datetime(cur_dt.tm_year, cur_dt.tm_mon,
                                        cur_dt.tm_mday, h, mn, s, 0)
            delta = datetime.timedelta(days=1)
            while next_dt.weekday() != d:
                next_dt = next_dt + delta
            timestamp = time.mktime(next_dt.timetuple())
            self.schedAt = timestamp
            self.schedArgs = (0, 0, d, h, mn, s)

        else:
            self.error('[Run Type=%s] not supported' % str(runType))
        return timestamp

    def isSuccessive(self):
        """
        Returns True if the task is successive, False otherwise
        """
        if self.schedType == SCHED_NOW_MORE:
            return True
        else:
            return False

    def isRecursive(self):
        """
        Returns True if the task is recursive, False otherwise
        """
        if self.schedType == SCHED_QUEUE or self.schedType == SCHED_QUEUE_AT:
            return False
        else:
            if self.schedType > Scheduler.SCHED_IN:
                return True
            else:
                return False

    def isPostponed(self):
        """
        Returns True is postponed, False otherwise
        """
        if self.schedType == Scheduler.SCHED_AT or self.schedType == Scheduler.SCHED_IN:
            return True
        else:
            return False

    def completeId(self, withTestPath=False):
        """
        Returns complete identifier
        Concatenation of variables: self.initTime,
        base64(self.testName) and self.userName
        With the following separator: .
        Example: 123622.770.dG1wMw==.admin
        """
        sep = "."

        testidentifier = self.testName
        if withTestPath:
            if self.testPath != "":
                testidentifier = self.testPath  # remove in v16

        # prepare a unique id for the test
        ret = []
        ret.append(
            time.strftime(
                "%Y-%m-%d_%H-%M-%S",
                time.localtime(
                    self.prepareTime)))
        ret.append(self.taskUuid)
        if sys.version_info > (3,):
            encoded = base64.b64encode(testidentifier.encode("utf8"))
            ret.append(encoded.decode("utf8"))
        else:
            ret.append(base64.b64encode(testidentifier.encode("utf8")))
        ret.append(self.userName)

        # join with . separator and return it
        return sep.join(ret)

    def toDict(self):
        """
        To dict data type
        """
        ret = {}
        ret['id'] = self.schedId
        if self.testPath != "":
            ret['name'] = self.testPath
        else:
            ret['name'] = self.testName
        ret['name'] = os.path.normpath(ret['name'])
        ret['user'] = self.userName
        ret['state'] = self.state
        ret['sched-id'] = self.schedType
        ret['sched-args'] = self.schedArgs
        ret['sched-nb'] = self.schedNb
        ret['sched-at'] = self.schedAt
        ret['start-at'] = self.startTime
        ret['stop-at'] = self.stopTime
        ret['duration'] = self.duration
        ret['recursive'] = self.isRecursive()
        ret['project-id'] = self.projectId
        ret['test-id'] = self.getTestID()
        ret['replay-id'] = self.replayId
        return ret

    def removeFinishedFile(self, envTmp=False):
        """
        Remove FINISHED file
        """
        self.trace('removing finished file')
        try:
            fp = "%s/%s" % (self.getPath(envTmp=envTmp), STATE_FINISHED)
            res = os.path.exists(os.path.normpath(fp))
            if res:
                os.remove(os.path.normpath(fp))
        except OSError as e:
            self.trace('Unable to remove finished file: %s' % str(e))
        except Exception as e:
            self.error('Unable to remove finished file: %s' % str(e))

    def setFINISHED(self, envTmp=False):
        """
        Called when the task is finished
        """
        if self.state != STATE_CANCELLED:
            self.trace('adding finished file')
            try:
                f = open(
                    "%s/%s" %
                    (self.getPath(
                        envTmp=envTmp),
                        STATE_FINISHED),
                    'w')
                f.close()
            except Exception as e:
                self.error(e)

        # Add the task on the history db and notify all connected users
        if Settings.getInt('Database', 'insert-test-history'):
            ret, lastinsertid = TaskMngr.addToHistory(task=self)
            toSend = list(self.toTuple())
            if ret is None:
                toSend.insert(0, 999999)  # error
            else:
                toSend.insert(0, lastinsertid)  # insert ok

            # new in v10, return only history task according to the user
            connected = self.ctx.instance().getUsersConnectedCopy()
            for cur_user in connected:
                prjs = ProjectsManager.instance().getProjects(user=cur_user)
                prjsDict = {}
                for prj in prjs:
                    prjsDict[prj['project_id']] = True
                if self.projectId in prjsDict:
                    data = ('task-history', ("add", [toSend]))
                    ESI.instance().notify(body=data, toUser=cur_user)
            del connected
            # end of new in v10

        if self.prepared:
            TaskMngr.onTaskFinished(self)

    def setState(self, state, envTmp=False):
        """
        Set the state: INIT, WAITING, RUNNING, COMPLETE, ERROR, KILLED, CANCELLED
        """
        self.mutex.acquire()
        self.state = state

        if state not in [STATE_INIT, STATE_WAITING]:
            try:
                f = open("%s/STATE" % self.getPath(envTmp=envTmp), 'w')
                f.write(state)
                f.close()
            except Exception as e:
                self.error("unable to write state file %s" % e)

        if self.schedId is not None:
            self.trace('task %s state: %s' % (self.schedId, state))
        else:
            self.trace('task state: %s' % state)

        # disable the task according to the state
        if state == STATE_DISABLED:
            self.enabled = False
        else:
            self.enabled = True

        if state == STATE_RUNNING:
            self.startTime = time.time()

            # save json test for rest api
            self.saveTestDescr()

            # Notify all connected users to remove event from the waiting list
            # on the client interface
            connected = self.ctx.instance().getUsersConnectedCopy()
            for cur_user in connected:
                self.trace("updating task waiting: %s" % cur_user)
                data = (
                    'task-waiting',
                    ("update",
                     TaskMngr.getWaiting(
                         user=cur_user)))
                ESI.instance().notify(body=data, toUser=cur_user)
            del connected

            # Notify all connected users to add event to running list on the client interface
            # new in v10
            connected = self.ctx.instance().getUsersConnectedCopy()
            for cur_user in connected:
                self.trace("updating task running: %s" % cur_user)
                data = (
                    'task-running',
                    ("update",
                     TaskMngr.getRunning(
                         user=cur_user)))
                ESI.instance().notify(body=data, toUser=cur_user)
            del connected
            # end of new in v10

        elif state in [STATE_CANCELLED, STATE_COMPLETE, STATE_ERROR, STATE_KILLED]:
            if self.startTime != "N/A":
                self.stopTime = time.time()
                self.duration = self.stopTime - self.startTime
                # notify all connected users to remove event to running list
                connected = self.ctx.instance().getUsersConnectedCopy()
                for cur_user in connected:
                    data = (
                        'task-running',
                        ("update",
                         TaskMngr.getRunning(
                             user=cur_user)))
                    ESI.instance().notify(body=data, toUser=cur_user)
                del connected
            self.setFINISHED(envTmp=envTmp)
        self.mutex.release()

    def cancelTest(self, removeFromContext=True):
        """
        Cancel the test
        """
        self.removeFromContext = removeFromContext
        if self.state == STATE_DISABLED:
            self.setState(state=STATE_CANCELLING)
            self.setState(state=STATE_CANCELLED)
            ret = True
        else:
            self.setState(state=STATE_CANCELLING)
            ret = TaskMngr.unregisterEvent(event=self.eventReg)
            if ret:
                self.setState(state=STATE_CANCELLED)
            else:
                self.setState(state=STATE_ERROR)
        return ret

    def getTestsPath(self):
        """
        Get the path of all tests
        """
        testsResultPath = '%s%s' % (Settings.getDirExec(),
                                    Settings.get('Paths', 'testsresults'))
        return os.path.normpath(testsResultPath)

    def generateTestID(self):
        """
        deprecated
        """
        ret = ''
        try:
            self.trace(self.getTestPath(withDate=True))
            hash = hashlib.md5()
            hash.update(self.getTestPath(withDate=True))
            ret = hash.hexdigest()
        except Exception as e:
            self.error("Unable to make hash for test id: %s" % str(e))
        return ret

    def getTestID(self):
        """
        """
        return self.taskUuid

    def getTestPath(self, withDate=True):
        """
        Get the path of the test
        """
        if withDate:
            return os.path.normpath("/%s/%s/%s" % (self.projectId,
                                                   time.strftime(
                                                       "%Y-%m-%d", time.localtime(self.prepareTime)),
                                                   self.completeId()))
        else:
            return self.completeId()

    def getPath(self, envTmp=False, fullPath=True, noDate=False):
        """
        Returns the complete path of the test
        """
        testsResultPath = '%s%s' % (Settings.getDirExec(),
                                    Settings.get('Paths', 'testsresults'))
        if envTmp:
            testsResultPath = '%s%s' % (Settings.getDirExec(),
                                        Settings.get('Paths', 'testsresults-tmp'))
        if fullPath:
            if noDate:
                return os.path.normpath(
                    "%s/%s" % (testsResultPath, self.completeId()))
            else:
                return os.path.normpath("%s/%s/%s/%s" % (testsResultPath,
                                                         self.projectId,
                                                         time.strftime(
                                                             "%Y-%m-%d", time.localtime(self.prepareTime)),
                                                         self.completeId()))
        else:
            if noDate:
                return "%s" % (self.completeId())
            else:
                return os.path.normpath("%s/%s/%s" % (self.projectId,
                                                      time.strftime(
                                                          "%Y-%m-%d", time.localtime(self.prepareTime)),
                                                      self.completeId()))

    def getDirToday(self, envTmp=False, fullPath=True):
        """
        Get the path of today directory
        """
        testResultPath = '%s%s' % (Settings.getDirExec(),
                                   Settings.get('Paths', 'testsresults'))
        if envTmp:
            testResultPath = '%s%s' % (Settings.getDirExec(),
                                       Settings.get('Paths', 'testsresults-tmp'))
        if fullPath:
            return os.path.normpath("%s/%s/%s" % (testResultPath,
                                                  self.projectId,
                                                  time.strftime("%Y-%m-%d", time.localtime(self.prepareTime)))
                                    )
        else:
            return os.path.normpath("%s/%s" % (self.projectId,
                                               time.strftime("%Y-%m-%d", time.localtime(self.prepareTime)))
                                    )

    def getDirProject(self, envTmp=False, fullPath=True):
        """
        Get the path of project directory
        """
        testResultPath = '%s%s' % (Settings.getDirExec(),
                                   Settings.get('Paths', 'testsresults'))
        if envTmp:
            testResultPath = '%s%s' % (Settings.getDirExec(),
                                       Settings.get('Paths', 'testsresults-tmp'))
        if fullPath:
            return os.path.normpath("%s/%s" % (testResultPath, self.projectId))
        else:
            return self.projectId

    def wrongParseToFile(self, te, subTest=False):
        """
        Called when the file to parse is invalid
        This file is saved on the tmp storage
        """
        try:
            tmpPath = '%s%s/Parsed' % (Settings.getDirExec(),
                                       Settings.get('Paths', 'tmp'))
            tmpPath = os.path.normpath(tmpPath)
            os.mkdir(tmpPath, 0o755)
        except Exception:
            pass
        # write wrong test
        fileName = '%s%s/Parsed/%s_%s_%s.BAD' % (Settings.getDirExec(),
                                                 Settings.get('Paths', 'tmp'),
                                                 self.testName,
                                                 self.userName,
                                                 self.getTestID())
        if subTest:
            fileName = '%s%s/Parsed/SubTE_%s_%s_%s.BAD' % (Settings.getDirExec(),
                                                           Settings.get(
                                                               'Paths', 'tmp'),
                                                           self.testName,
                                                           self.userName,
                                                           self.getTestID())

        fileName = os.path.normpath(fileName)

        f = open(fileName, 'wb')

        if sys.version_info > (3,):  # python3 support
            te = te.encode("utf8")

        f.write(te)
        f.close()

    def parseTestDesign(self):
        """
        """
        self.prepareTime = time.time()
        ret = {}
        ret["error"] = False
        ret["error-details"] = ""
        ret['design'] = ''
        ret['design-xml'] = ''
        envTmp = True
        cleanupTmp = True
        try:
            # Prepare the destination storage result
            self.trace(
                "Parse test design: preparing the destination storage result")
            try:
                # create project folder
                try:
                    prjFolder = self.getDirProject(envTmp=envTmp)
                    if not os.path.exists(prjFolder):
                        os.mkdir(prjFolder, 0o755)
                except Exception as e:
                    self.trace(
                        "folder %s already exist: %s" %
                        (prjFolder, str(e)))
                else:

                    # create date folder
                    try:
                        mainDir = self.getDirToday(envTmp=envTmp)
                        if not os.path.exists(mainDir):
                            os.mkdir(mainDir, 0o755)
                    except Exception as e:
                        self.trace(
                            "dir %s already exist: %s" %
                            (mainDir, str(e)))
                    else:
                        os.mkdir(
                            self.getPath(
                                envTmp=envTmp,
                                noDate=False),
                            0o755)
            except OSError as e:
                cleanupTmp = False
                self.error(
                    'Parse test design: unable to prepare the directory, system error: %s' %
                    str(e))
                ret["error"] = True
                ret["error-details"] = "Server"
            else:

                # Creation te
                self.trace(
                    "Parse test design: creating the test executable for test design check")
                try:
                    te = TestModelDesign.createTestDesign(
                        dataTest=self.dataTest,
                        userName=self.userName,
                        testName=self.testName,
                        trPath=self.getTestPath(withDate=True),
                        logFilename=self.completeId(),
                        userId=self.userId,
                        projectId=self.projectId,
                        testId=self.testId,
                        runningAgents=AgentsManager.instance().getRunning(),
                        testLocation=self.getTestLocation(),
                        parametersShared=self.ctx.instance().getTestEnvironment(user=self.userName,
                                                                                ),
                        var_path=Settings.get('Paths', 'testsresults-tmp')
                    )
                except Exception as e:
                    cleanupTmp = False
                    self.error(
                        'Parse test design: unable to create te: %s' %
                        str(e))
                    ret["error"] = True
                    ret["error-details"] = "Server"
                else:

                    # Saving
                    self.trace(
                        "Parse test design: saving the test executable on the disk")
                    try:
                        # write test
                        f = open("%s/%s" % (self.getPath(envTmp=envTmp, noDate=False),
                                            self.completeId()), 'wb')
                        f.write(te)
                        f.close()

                        # write log
                        f = open("%s/test.log" % self.getPath(envTmp=envTmp,
                                                              noDate=False), 'w')
                        f.close()

                        # write tests settings
                        shutil.copy('%s/test.ini' % Settings.getDirExec(),
                                    "%s/test.ini" % self.getPath(envTmp=envTmp,
                                                                 noDate=False))

                        # tunning settings
                        cfgtest_path = "%s/test.ini" % self.getPath(envTmp=envTmp,
                                                                    noDate=False)
                        cfgparser = ConfigParser.RawConfigParser()
                        cfgparser.read(cfgtest_path)
                        cfgparser.set('Paths', 'templates', "%s/%s/" % (Settings.getDirExec(),
                                                                        Settings.get('Paths', 'templates')))

                        cfgtest = open(cfgtest_path, 'w')
                        cfgparser.write(cfgtest)
                        cfgtest.close()
                    except OSError as e:
                        cleanupTmp = False
                        self.error(
                            'Parse test design: unable to save te, system error: %s' %
                            str(e))
                        ret["error"] = True
                        ret["error-details"] = "Server"
                    except Exception as e:
                        cleanupTmp = False
                        self.error(
                            'Parse test design: unable to save te: %s' %
                            str(e))
                        ret["error"] = True
                        ret["error-details"] = "Server"
                    else:

                        # Compilation
                        self.trace(
                            "Parse test design: compile the test executable")
                        try:
                            if sys.version_info > (3,):
                                te = te.decode("utf8")
                            parser.suite(te).compile()
                        except SyntaxError as e:
                            cleanupTmp = False
                            self.trace(
                                'Parse test design: unable to compile syntax te: %s' % e)
                            ret["error"] = True
                            ret["error-details"] = "Syntax"
                        except Exception:
                            cleanupTmp = False
                            self.error(
                                'Parse test design: unable to compile te: %s' %
                                getBackTrace())
                            ret["error"] = True
                            ret["error-details"] = "Server"
                        else:
                            self.trace("Parse test design: fork and executing")
                            # Execute
                            if platform.system() == "Windows":
                                executable = Settings.get('Bin', 'python-win')
                            else:
                                executable = Settings.get('Bin', 'python')
                            cmdOptions = [str(self.schedId), str(
                                self.testId), str(self.replayId)]

                            # Construct the final execution line
                            args = [executable]
                            exec_path = "%s/%s" % (self.getPath(envTmp=envTmp, noDate=False),
                                                   self.completeId())
                            args.append(os.path.normpath(exec_path))
                            args = args + cmdOptions

                            # create sh file
                            if platform.system() == "Windows":
                                launch_cmd = "launch.bat"
                            else:
                                launch_cmd = "launch.sh"
                            launch_path = "%s/%s" % (self.getPath(envTmp=envTmp, noDate=False),
                                                     launch_cmd)

                            f = open(os.path.normpath(launch_path), 'w')
                            f.write(" ".join(args))
                            f.close()

                            # Fork and run it
                            try:
                                p = subprocess.Popen(args)
                                self.tpid = p.pid
                                TaskMngr.addChildPid(pid=p.pid)
                            except Exception:
                                self.error('unable to fork te')
                                ret["error"] = True
                                ret["error-details"] = "Fork"
                            else:
                                p.wait()
                                retcode = p.returncode

                                if retcode == 0:
                                    self.trace("Parse test design: fork ok")
                                    # read result
                                    ret['design'] = self.getTestDesign(
                                        returnXml=False, envTmp=True)
                                    ret['design-xml'] = self.getTestDesign(
                                        returnXml=True, envTmp=True)
                                else:
                                    cleanupTmp = False
                                    self.error(
                                        'unknown return code received: %s' %
                                        str(retcode))
                                    ret["error"] = True
                                    ret["error-details"] = "Return"

        except Exception as e:
            self.error("unable to parse test design: %s" % str(e))
            ret["error"] = True
            ret["error-details"] = "Global"

        # Remove the test from temp storage
        if cleanupTmp:
            try:
                if os.path.exists(self.getPath(envTmp=envTmp, noDate=False)):
                    shutil.rmtree(self.getPath(envTmp=envTmp, noDate=False))
            except Exception as e:
                self.error(
                    "Parse test design: unable to delete the tmp test: %s" %
                    str(e))

        return ret

    def parseTest(self):
        """
        Parse the test
        Return False if the syntax is wrong
        """
        self.trace("Parse test: create all sub test executable")
        sub_tes = []
        try:
            # detect the type of test
            isTu = False
            isTa = False
            isTp = False
            isTg = False
            isTs = False

            if self.dataTest["test-extension"] == "tux":
                isTu = True
            if self.dataTest["test-extension"] == "tsx":
                isTs = True
            if self.dataTest["test-extension"] == "tpx":
                isTp = True
            if self.dataTest["test-extension"] == "tgx":
                isTg = True

            if isTu or isTs or isTa:
                sub_descr = self.dataTest['test-properties']['descriptions']['description']
                subte = TestModelSub.createSubTest(dataTest=self.dataTest,
                                                   trPath=self.getTestPath(),
                                                   descriptions=sub_descr,
                                                   isTestUnit=isTu
                                                   )
                sub_tes.append((isTu, isTa, self.testName, subte, ''))

            if isTp or isTg:
                insideTp = ''
                for ts in self.dataTest['test-execution']:
                    isSubTu = False
                    isSubTa = False
                    if 'extension' in ts:
                        if ts['extension'] == RepoManager.TEST_UNIT_EXT:
                            isSubTu = True
                        if ts['extension'] == RepoManager.TEST_PLAN_EXT and ts['separator'] == 'started':
                            while ts['testpath'][:1] == '/':
                                ts['testpath'] = ts['testpath'][1:]
                            insideTp = '%s:/%s/%s.%s' % (
                                ts['testproject'],
                                ts['testpath'],
                                ts['testname'],
                                ts['extension'],
                            )

                        if ts['extension'] == RepoManager.TEST_PLAN_EXT and ts['separator'] == 'terminated':
                            insideTp = ''
                    if ts['enable'] == TestModelCommon.TS_ENABLED:
                        sub_descr = self.dataTest['test-properties']['descriptions']['description']
                        subte = TestModelSub.createSubTest(dataTest=ts,
                                                           trPath=self.getTestPath(),
                                                           descriptions=sub_descr,
                                                           isTestUnit=isSubTu,
                                                           isTestPlan=isTp,
                                                           isTestGlobal=isTg)
                        sub_tes.append(
                            (isSubTu, isSubTa, ts['file'], subte, insideTp))
        except Exception as e:
            self.error("parse test - unable to prepare sub te: %s" % str(e))
            return (False, {'msg': str(e)})
        else:
            self.trace("Parse test: creating the main te")
            startTestLine = 0
            te = ''
            try:
                te_shared = self.ctx.instance().getTestEnvironment(user=self.userName)
                te = TestModel.createTestExecutable(dataTest=self.dataTest,
                                                    userName=self.userName,
                                                    testName=self.testName,
                                                    trPath=self.getTestPath(),
                                                    logFilename=self.completeId(),
                                                    userId=self.userId,
                                                    projectId=self.projectId,
                                                    subTEs=len(sub_tes),
                                                    channelId=self.channelId,
                                                    parametersShared=te_shared,
                                                    stepByStep=self.stepByStep,
                                                    breakpoint=self.breakpoint,
                                                    testId=self.testId,
                                                    testLocation=self.getTestLocation(),
                                                    runningAgents=AgentsManager.instance().getRunning(),
                                                    taskUuid=self.taskUuid,
                                                    var_path=Settings.get(
                                                        'Paths', 'testsresults')
                                                    )
                if sys.version_info > (3,):
                    te = te.decode("utf8")
            except Exception as e:
                self.error("parse test - unable to prepare te: %s" % str(e))
                self.wrongParseToFile(te=te)
                return (False, {'msg': str(e)})
            else:
                self.trace("compile all sub test executable")
                for i in xrange(len(sub_tes)):
                    issub_tu, issub_ta, subtest_name, subtest_val, inside_tp = sub_tes[i]

                    if sys.version_info > (3,):
                        subtest_val = subtest_val.decode("utf8")

                    # find startline of the test
                    if issub_tu or issub_ta:
                        beginTe, leftTe = subtest_val.split(
                            'class TESTCASE(TestCase):')
                    else:
                        beginTe, leftTe = subtest_val.split(
                            '# !! test injection')

                    startTestLine = len(beginTe.splitlines()) + 1

                    # compile
                    try:
                        parser.suite(subtest_val).compile()
                    except SyntaxError as e:
                        self.trace("sub test (id=%s, testname=%s) syntax error: %s" % (i,
                                                                                subtest_name,
                                                                                str(e)))
                        line_err = (e.lineno - startTestLine) + 1
                        # set the lineno to None to remove it from exception
                        # text
                        e.lineno = None
                        self.wrongParseToFile(te=subtest_val, subTest=True)
                        return (False, {'msg': str(e),
                                        'line': line_err,
                                        'testname': subtest_name,
                                        'parent-testname': inside_tp})

                    except SystemError as e:
                        self.error(
                            "sub test server limitation raised: %s" %
                            str(e))
                        return (
                            False, {'msg': "Limitation: the test (%s) is too big!" % subtest_name})
                    except Exception as e:
                        self.error(
                            'unable to parse sub test %s' %
                            getBackTrace())
                        e.lineno = None
                        self.wrongParseToFile(te=subtest_val, subTest=True)
                        return (False, {'msg': str(e),
                                        'testname': subtest_name})

                self.trace('Parse test: compiling main te')
                try:
                    parser.suite(te).compile()
                except SyntaxError as e:
                    self.trace("syntax error: %s" % str(e))
                    self.wrongParseToFile(te=te)
                    return (False, {'msg': str(e), 'line': e.lineno})

                except SystemError as e:
                    self.error("server limitation raised: %s" % str(e))
                    return (
                        False, {'msg': "Limitation: this test is too big!"})
                except Exception as e:
                    self.error('unable to parse test %s' % getBackTrace())
                    e.lineno = None
                    self.wrongParseToFile(te=te)
                    return (False, {'msg': str(e)})

            return (True, {})

    def getTestLocation(self):
        """
        """
        return os.path.dirname(os.path.normpath(self.testPath))

    def initPrepare(self):
        """
        """
        self.prepareTime = time.time()
        self.prepared = False
        self.trace("Prepare time: %s" % self.prepareTime)
        self.trace(
            "Human prepare time: %s" %
            time.strftime(
                "%Y-%m-%d",
                time.localtime(
                    self.prepareTime)))
        return True

    def prepare(self, envTmp=False):
        """
        Prepare the test
        - Prepare storage
        - Creation
        - Saving
        - Compilation
        """
        self.initPrepare()

        # Prepare the destination storage result
        self.trace("Preparing the destination storage result")
        try:
            if envTmp:
                try:
                    tmpPath = '%s%s' % (Settings.getDirExec(),
                                        Settings.get('Paths', 'testsresults-tmp'))
                    tmpPath = os.path.normpath(tmpPath)
                    if not os.path.exists(tmpPath):
                        os.mkdir(tmpPath, 0o755)
                except Exception as e:
                    self.trace("dir %s already exist: %s" % (tmpPath, str(e)))
            # create main dir
            try:
                mainDir = self.getDirProject(envTmp=envTmp)
                if not os.path.exists(mainDir):
                    os.mkdir(mainDir, 0o755)
            except Exception as e:
                self.trace(
                    "dir project %s already exist: %s" %
                    (mainDir, str(e)))
            try:
                mainDir = self.getDirToday(envTmp=envTmp)
                if not os.path.exists(mainDir):
                    os.mkdir(mainDir, 0o755)
            except Exception as e:
                self.trace("dir date %s already exist: %s" % (mainDir, str(e)))

            # create specific folder
            os.mkdir(self.getPath(envTmp=envTmp), 0o755)

        except OSError as e:
            self.error(
                'unable to prepare the directory, system error: %s' %
                str(e))
            self.setState(state=STATE_ERROR, envTmp=envTmp)
            raise Exception(e.strerror)
        except Exception as e:
            self.error('unable to prepare the directory: %s' % e)
            self.setState(state=STATE_ERROR, envTmp=envTmp)
            raise Exception(getBackTrace())

        # Creation
        self.trace("Create all sub test executable")
        sub_tes = []
        try:
            dataTest = self.dataTest
            if envTmp:  # not alter data in tmp env
                dataTest = copy.deepcopy(self.dataTest)

            # detect the type of test
            isTu = False
            isTp = False
            isTg = False
            isTs = False

            if self.dataTest["test-extension"] == "tux":
                isTu = True
            if self.dataTest["test-extension"] == "tsx":
                isTs = True
            if self.dataTest["test-extension"] == "tpx":
                isTp = True
            if self.dataTest["test-extension"] == "tgx":
                isTg = True

            if isTu or isTs:
                subte_descr = dataTest['test-properties']['descriptions']['description']
                subte = TestModelSub.createSubTest(dataTest=dataTest,
                                                   trPath=self.getTestPath(),
                                                   descriptions=subte_descr,
                                                   isTestUnit=isTu
                                                   )
                sub_tes.append(subte)

            if isTp or isTg:
                for ts in dataTest['test-execution']:
                    isSubTu = False
                    if 'extension' in ts:
                        if ts['extension'] == RepoManager.TEST_UNIT_EXT:
                            isSubTu = True

                    if ts['enable'] == TestModelCommon.TS_ENABLED:
                        subte_descr = dataTest['test-properties']['descriptions']['description']
                        subte = TestModelSub.createSubTest(dataTest=ts,
                                                           descriptions=subte_descr,
                                                           trPath=self.getTestPath(),
                                                           isTestUnit=isSubTu,
                                                           isTestPlan=isTp,
                                                           isTestGlobal=isTg)
                        sub_tes.append(subte)

        except Exception:
            self.error('unable to create sub te')
            self.setState(state=STATE_ERROR, envTmp=envTmp)
            raise Exception(getBackTrace())

        self.trace("nb sub test: %s" % len(sub_tes))
        self.trace("Create the main test executable")
        try:
            dataTest = self.dataTest
            if envTmp:  # not alter data in tmp env
                dataTest = copy.deepcopy(self.dataTest)

            te = TestModel.createTestExecutable(
                dataTest=dataTest,
                userName=self.userName,
                testName=self.testName,
                trPath=self.getTestPath(),
                logFilename=self.completeId(),
                userId=self.userId,
                projectId=self.projectId,
                subTEs=len(sub_tes),
                channelId=self.channelId,
                parametersShared=self.ctx.instance().getTestEnvironment(user=self.userName),
                stepByStep=self.stepByStep,
                breakpoint=self.breakpoint,
                testId=self.testId,
                testLocation=self.getTestLocation(),
                runningAgents=AgentsManager.instance().getRunning(),
                taskUuid=self.taskUuid,
                var_path=Settings.get('Paths', 'testsresults')
            )
        except Exception as e:
            self.error('unable to create the main te: %s' % e)
            self.setState(state=STATE_ERROR, envTmp=envTmp)
            raise Exception(getBackTrace())

        # Saving
        self.trace("Save the test executable on the disk")
        try:
            self.trace("Writing init file for test executable")
            # write test
            f = open("%s/__init__.py" % self.getPath(envTmp=envTmp), 'w')
            f.write("")
            f.close()

            self.trace("Writing all sub te")
            for i in xrange(len(sub_tes)):
                f = open("%s/SubTE%s.py" %
                         (self.getPath(envTmp=envTmp), i), 'wb')
                f.write(sub_tes[i])
                f.close()

            self.trace("Writing main path of the te")
            # write test path
            f = open(
                "%s/TESTPATH" %
                self.getPath(
                    envTmp=envTmp),
                'w')  # change in v16
            f.write("%s" % self.testPath)
            f.close()

            self.trace("Writing main te")
            # write test
            f = open(
                "%s/MainTE.py" %
                self.getPath(
                    envTmp=envTmp),
                'wb')  # change in v16
            f.write(te)
            f.close()

            self.trace("Writing task uuid")
            # write log
            f = open("%s/TASKID" % self.getPath(envTmp=envTmp), 'w')
            f.write("%s" % self.taskUuid)
            f.close()
            if not envTmp:
                self.saveTestDescr()
                RepoArchives.instance().cacheUuid(taskId=self.taskUuid,
                                                  testPath=self.getPath(envTmp=False, fullPath=False))

            self.trace("Writing log file")
            # write log
            f = open("%s/test.log" % self.getPath(envTmp=envTmp), 'wb')
            f.close()

            # write tests settings
            shutil.copy('%s/test.ini' % Settings.getDirExec(),
                        "%s/test.ini" % self.getPath(envTmp=envTmp))

            # tunning settings
            cfgtest_path = "%s/test.ini" % self.getPath(envTmp=envTmp)
            cfgparser = ConfigParser.RawConfigParser()
            cfgparser.read(cfgtest_path)

            path_storage = "%s/%s/" % (StorageDataAdapters.instance().getStoragePath(),
                                       self.getPath(fullPath=False))
            cfgparser.set('Paths', 'tmp', os.path.normpath(path_storage))

            path_template = "%s/%s/" % (Settings.getDirExec(),
                                        Settings.get('Paths', 'templates'))
            cfgparser.set(
                'Paths',
                'templates',
                os.path.normpath(path_template))

            cfgparser.set(
                'Paths',
                'result',
                "%s" %
                self.getPath(
                    envTmp=envTmp))

            path_adapters = "%s/%s/" % (Settings.getDirExec(),
                                        Settings.get('Paths', 'packages-sutadapters'))
            cfgparser.set(
                'Paths',
                'sut-adapters',
                os.path.normpath(path_adapters))

            path_public = "%s/%s/" % (Settings.getDirExec(),
                                      Settings.get('Paths', 'public'))
            cfgparser.set('Paths', 'public', os.path.normpath(path_public))

            cfgparser.set(
                'Tests_Framework',
                'test-in-background',
                "%s" % int(
                    self.background))

            if self.debugActivated:
                cfgparser.set('Trace', 'level', "DEBUG")
            cfgtest = open(cfgtest_path, 'w')
            cfgparser.write(cfgtest)
            cfgtest.close()
        except OSError as e:
            self.error('unable to save te, system error: %s' % str(e))
            self.setState(state=STATE_ERROR, envTmp=envTmp)
            raise Exception(e.strerror)
        except Exception:
            self.error('unable to save te')
            self.setState(state=STATE_ERROR, envTmp=envTmp)
            raise Exception(getBackTrace())

        # Notify all connected users to update archives
        # if it is not a run in the temporary env or
        # if it is not necessary to keep test result
        if not envTmp:
            if Settings.getInt('Notifications', 'archives'):
                if not self.noKeepTr:
                    # msg (dest, (action, data))
                    m = [{"type": "folder", "name": time.strftime("%Y-%m-%d", time.localtime(self.prepareTime)),
                          "project": "%s" % self.projectId,
                          "content": [{"project": "%s" % self.projectId,
                                       "type": "folder",
                                       "name": self.completeId(),
                                       "virtual-name": self.completeId(withTestPath=True),
                                       "content": []}]}]
                    notif = {}
                    notif['archive'] = m
                    data = ('archive', (None, notif))
                    ESI.instance().notifyByUserAndProject(body=data,
                                                          admin=True,
                                                          monitor=False,
                                                          tester=True,
                                                          projectId="%s" % self.projectId)

        # Compilation
        if self.syntaxOK:
            self.trace(
                "Task[Tmp=%s] parse/compile: already done before" %
                envTmp)
        else:
            self.trace(
                "Task[Tmp=%s] parse/compile all sub test executable" %
                envTmp)
            t0 = time.time()
            for i in xrange(len(sub_tes)):
                try:
                    if sys.version_info > (3,):
                        parser.suite(sub_tes[i].decode("utf8")).compile()
                    else:
                        parser.suite(sub_tes[i]).compile()
                except SyntaxError as e:
                    e.lineno = None
                    self.trace('unable to compile sub te syntax: %s' % e)
                    self.setState(state=STATE_ERROR, envTmp=envTmp)
                    raise Exception(e)
                except SystemError as e:
                    self.error("Sub te server limitation raised: %s" % str(e))
                    self.setState(state=STATE_ERROR, envTmp=envTmp)
                    raise Exception("Limitation: this test is too big!")
                except Exception as e:
                    self.error('unable to compile sub te: %s' % getBackTrace())
                    self.setState(state=STATE_ERROR, envTmp=envTmp)
                    raise Exception(e)

            self.trace("Compile the main test executable")
            try:
                if sys.version_info > (3,):
                    parser.suite(te.decode("utf8")).compile()
                else:
                    parser.suite(te).compile()
            except SyntaxError as e:
                e.lineno = None
                self.trace('unable to compile syntax te: %s' % e)
                self.setState(state=STATE_ERROR, envTmp=envTmp)
                raise Exception(e)
            except SystemError as e:
                self.error("server limitation raised: %s" % str(e))
                self.setState(state=STATE_ERROR, envTmp=envTmp)
                raise Exception("Limitation: this test is too big!")
            except Exception as e:
                self.error('unable to compile te: %s' % getBackTrace())
                self.setState(state=STATE_ERROR, envTmp=envTmp)
                raise Exception(e)
            self.trace(
                "Task[Tmp=%s] parse/compile test terminated in %s sec." %
                (envTmp, (time.time() - t0)))

        # Remove the test from temp storage
        if envTmp:
            try:
                if os.path.exists(self.getPath(envTmp=envTmp)):
                    shutil.rmtree(self.getPath(envTmp=envTmp))
            except Exception as e:
                self.error("unable to delete the tmp test: %s" % str(e))

        self.setPrepared()
        self.trace(
            "Task[Tmp=%s] prepared in %s sec." %
            (envTmp, (time.time() - self.prepareTime)))
        return self.prepared

    def getTestDesign(self, returnXml=False, envTmp=False):
        """
        Return the report of the task
        """
        designs = ''
        if envTmp:
            testResultPath = '%s%s' % (Settings.getDirExec(),
                                       Settings.get('Paths', 'testsresults-tmp'))
        else:
            testResultPath = '%s%s' % (Settings.getDirExec(),
                                       Settings.get('Paths', 'testsresults'))
        testResultPath = os.path.normpath(testResultPath)

        fileName = "%s/%s/%s/%s/%s_%s" % (testResultPath,
                                          self.projectId,
                                          time.strftime(
                                              "%Y-%m-%d",
                                              time.localtime(
                                                  self.prepareTime)),
                                          self.completeId(),
                                          self.testName,
                                          str(self.replayId))
        fileName = os.path.normpath(fileName)

        try:
            if returnXml:
                f = open(
                    '%s.%s' %
                    (fileName, RepoManager.TEST_RESULT_DESIGN_XML_EXT), 'r')
            else:
                f = open(
                    '%s.%s' %
                    (fileName, RepoManager.TEST_RESULT_DESIGN_EXT), 'r')
            designs = f.read()
            if sys.version_info > (3,):
                pass
            else:
                designs = designs.decode('utf8')
            f.close()
        except Exception as e:
            self.error("open test result design failed: %s" % str(e))

        return designs

    def getTestReport(self, returnXml=False, basicReport=False):
        """
        Return the report of the task
        """
        reports = ''
        testResultPath = '%s%s' % (Settings.getDirExec(),
                                   Settings.get('Paths', 'testsresults'))
        testResultPath = os.path.normpath(testResultPath)

        fileName = "%s/%s/%s/%s/%s_%s" % (testResultPath,
                                          self.projectId,
                                          time.strftime(
                                              "%Y-%m-%d",
                                              time.localtime(
                                                  self.prepareTime)),
                                          self.completeId(),
                                          self.testName,
                                          str(self.replayId))
        fileName = os.path.normpath(fileName)

        try:

            if returnXml:
                reportExt = RepoManager.TEST_RESULT_REPORT_XML_EXT
                f = open('%s.%s' % (fileName, reportExt), 'r')
            else:
                reportExt = RepoManager.TEST_RESULT_REPORT_EXT
                if basicReport:
                    reportExt = RepoManager.TEST_RESULT_BASIC_REPORT_EXT
                f = open('%s.%s' % (fileName, reportExt), 'r')
            reports = f.read()
            if sys.version_info > (3,):
                pass
            else:
                reports = reports.decode('utf8')
            f.close()
        except Exception as e:
            self.error("open test result report failed: %s" % str(e))

        return reports

    def getTestVerdict(self, returnXml=False):
        """
        Return the verdict of the task
        """
        verdict = ''
        testResultPath = '%s%s' % (Settings.getDirExec(),
                                   Settings.get('Paths', 'testsresults'))
        testResultPath = os.path.normpath(testResultPath)

        fileName = "%s/%s/%s/%s/%s_%s" % (testResultPath,
                                          self.projectId,
                                          time.strftime(
                                              "%Y-%m-%d",
                                              time.localtime(
                                                  self.prepareTime)),
                                          self.completeId(),
                                          self.testName,
                                          str(self.replayId))
        fileName = os.path.normpath(fileName)

        try:
            if returnXml:
                f = open(
                    '%s.%s' %
                    (fileName, RepoManager.TEST_RESULT_VERDICT_XML_EXT), 'r')
            else:
                f = open(
                    '%s.%s' %
                    (fileName, RepoManager.TEST_RESULT_VERDICT_EXT), 'r')
            verdict = f.read()
            if sys.version_info > (3,):
                pass
            else:
                verdict = verdict.decode('utf8')
            f.close()
        except Exception as e:
            self.error("open test result verdict failed: %s" % str(e))

        return verdict

    def getFileResultPath(self):
        """
        This function is used to add a comment on a test result
        """
        testResultPath = '%s%s' % (
            Settings.getDirExec(), Settings.get('Paths', 'testsresults'))
        testResultPath = os.path.normpath(testResultPath)

        dirTask = "%s/%s/%s/" % (self.projectId,
                                 time.strftime(
                                     "%Y-%m-%d",
                                     time.localtime(
                                         self.prepareTime)),
                                 self.completeId())
        dirTask = os.path.normpath(dirTask)

        # Search the file on the disk because
        # the number of current comment can
        # be different that the value known by the user
        trxFile = 'undefined'
        for f in os.listdir(os.path.normpath("%s/%s" %
                                             (testResultPath, dirTask))):
            if f.startswith("%s_%s" % (self.testName, str(
                    self.replayId))) and f.endswith('trx'):
                trxFile = "%s%s" % (dirTask, f)
        return trxFile

    def notifResultLog(self):
        """
        Notify all users if new file is available
        Compress txt log to gzip
        filename = testname_replayid_nbcomments_verdict.testresult
        """
        testResultPath = '%s%s' % (Settings.getDirExec(),
                                   Settings.get('Paths', 'testsresults'))
        testResultPath = os.path.normpath(testResultPath)

        dirTask = "%s/%s/%s/" % (self.projectId,
                                 time.strftime(
                                     "%Y-%m-%d",
                                     time.localtime(
                                         self.prepareTime)),
                                 self.completeId())
        dirTask = os.path.normpath(dirTask)

        fileName = "%s/%s/%s/%s/%s_%s" % (testResultPath,
                                          self.projectId,
                                          time.strftime(
                                              "%Y-%m-%d",
                                              time.localtime(
                                                  self.prepareTime)),
                                          self.completeId(),
                                          self.testName,
                                          str(self.replayId))
        fileName = os.path.normpath(fileName)

        try:
            os.path.getsize("%s.log" % fileName)
        except Exception as e:
            self.error(e)
            return

        # read events from log
        f = open("%s.log" % fileName, 'r')
        read_data = f.read()
        f.close()

        # read events from log
        f2 = open("%s.hdr" % fileName, 'r')
        read_header = f2.read()
        f2.close()

        # get final result
        # this file "VERDICT" is created
        # by the test library throught
        # the function "log_script_stopped"
        verdict = 'UNDEFINED'
        path_verdict = "%s/%s/%s/%s/" % (testResultPath,
                                         self.projectId,
                                         time.strftime(
                                             "%Y-%m-%d",
                                             time.localtime(
                                                 self.prepareTime)),
                                         self.completeId())
        path_verdict = os.path.normpath(path_verdict)
        if os.path.exists(os.path.normpath("%s/VERDICT_PASS" % path_verdict)):
            verdict = 'PASS'
        if os.path.exists(os.path.normpath("%s/VERDICT_FAIL" % path_verdict)):
            verdict = 'FAIL'

        # fix issue remove global verdict on verdict, fix in v11.2
        try:
            os.remove(
                os.path.normpath(
                    "%s/VERDICT_%s" %
                    (path_verdict, verdict)))
        except Exception:
            pass

        try:
            f = open(os.path.normpath("%s/RESULT" % path_verdict), 'w')
            f.write(verdict)
            f.close()

            if verdict == "PASS":
                self.resultsStats["passed"] += 1
            if verdict == "FAIL":
                self.resultsStats["failed"] += 1
            if verdict == "UNDEFINED":
                self.resultsStats["undefined"] += 1

            self.saveTestDescr()
        except Exception as e:
            self.error("unable to write result file %s" % e)
        # end of new

        self.verdict = verdict

        # construct data model
        dataModel = TestResult.DataModel(testResult=read_data,
                                         testHeader=read_header)
        nbComments = 0
        filenameTrx = "%s_%s_%s.%s" % (fileName,
                                       verdict,
                                       str(nbComments),
                                       RepoManager.TEST_RESULT_EXT)
        f = open(os.path.normpath(filenameTrx), 'wb')
        raw_data = dataModel.toXml()
        if raw_data is not None:
            if sys.version_info > (3,):  # python3 support
                raw_data = bytes(raw_data, "utf8")
            f.write(zlib.compress(raw_data))
        f.close()

        # optimization remove log file in v12
        try:
            os.remove(os.path.normpath("%s.log" % fileName))
        except Exception:
            pass
        # end of new

        if Settings.getInt('Notifications', 'archives'):
            if self.noKeepTr and self.verdict != 'PASS':
                self.trace(
                    'no keep test result activated: notify because the result is not pass')
                # msg (dest, (action, data))
                m = [{"type": "folder",
                      "project": "%s" % self.projectId,
                      "name": time.strftime("%Y-%m-%d", time.localtime(self.prepareTime)),
                      "content": [{"project": "%s" % self.projectId,
                                   "type": "folder",
                                   "name": self.completeId(),
                                   "content": [],
                                   "virtual-name": self.completeId(withTestPath=True)}]}]
                notif = {}
                notif['archive'] = m
                data = ('archive', (None, notif))

                ESI.instance().notifyByUserAndProject(body=data,
                                                      admin=True,
                                                      monitor=False,
                                                      tester=True,
                                                      projectId="%s" % self.projectId)

            if self.noKeepTr and self.verdict == 'PASS':
                self.trace(
                    'no keep test result activated: do not notify because the result is pass')
            else:
                size_ = os.path.getsize(filenameTrx)
                notif = {}
                m = [{"type": "folder",
                      "name": time.strftime("%Y-%m-%d", time.localtime(self.prepareTime)),
                      "project": "%s" % self.projectId,
                      "content": [{"type": "folder",
                                   "name": self.completeId(),
                                   "project": "%s" % self.projectId,
                                   "virtual-name": self.completeId(withTestPath=True),
                                   "content": [{"type": "file", "project": "%s" % self.projectId,
                                                "name": "%s_%s_%s_%s.%s" % (self.testName,
                                                                            str(self.replayId),
                                                                            verdict,
                                                                            str(nbComments),
                                                                            RepoManager.TEST_RESULT_EXT),
                                                'size': str(size_)}]}]}]
                notif['archive'] = m
                notif['stats-repo-archives'] = {'nb-zip': 0, 'nb-trx': 1, 'nb-tot': 1,
                                                'mb-used': RepoArchives.instance().getSizeRepoV2(folder=RepoArchives.instance().testsPath),
                                                'mb-free': RepoArchives.instance().freeSpace(p=RepoArchives.instance().testsPath)}
                data = ('archive', (None, notif))

                ESI.instance().notifyByUserAndProject(body=data,
                                                      admin=True,
                                                      monitor=False,
                                                      tester=True,
                                                      projectId="%s" % self.projectId)

        # not necessary to keep test results logs, delete it
        if self.noKeepTr:
            if self.verdict == 'PASS':
                try:
                    trPath = os.path.normpath(
                        '%s/%s' %
                        (testResultPath, dirTask))
                    if os.path.exists(trPath):
                        shutil.rmtree(trPath)
                except Exception as e:
                    self.error(
                        "unable to delete the test result (no keep tr option on result pass) %s" %
                        str(e))

    def run(self, needPrepare=True):
        """
        Executes Test
        Following arguments are sent to the test executable
            - test directory
            - Complete Test ID (just name)
            - Test Id on server
            - Test Name
            - Test Id
            - Replay Id
        """
        # this run is a replay is greater than zero, then the test is already
        # ready
        if self.replayId > 0:
            testready = True
        else:
            if not needPrepare:
                testready = True
            else:
                # first run, prepare test on final environment
                try:
                    testready = self.prepare(envTmp=False)
                except Exception:
                    self.error('unable to prepare test')
                    self.setState(state=STATE_ERROR)
                    raise Exception(getBackTrace())

        # create temp adapters data storage
        StorageDataAdapters.instance().initStorage()

        # folder "completeid" is removed if already exists
        StorageDataAdapters.instance().initSubStorage(dirProject=self.projectId,
                                                      dirToday=time.strftime(
                                                          "%Y-%m-%d", time.localtime(self.prepareTime)),
                                                      dirTest=self.completeId())

        if testready:
            self.setState(state=STATE_RUNNING)
            # Prepare arguments
            if platform.system() == "Windows":
                executable = Settings.get('Bin', 'python-win')
            else:
                executable = Settings.get('Bin', 'python')
            cmdOptions = [str(self.schedId), str(
                self.testId), str(self.replayId)]

            # Construct the final execution line
            args = [executable]
            args.append(os.path.normpath("%s/MainTE.py" % self.getPath()))
            args = args + cmdOptions

            # create sh file
            if platform.system() == "Windows":
                launch_cmd = "launch.bat"
            else:
                launch_cmd = "launch.sh"
            launch_path = "%s/%s" % (self.getPath(), launch_cmd)

            f = open(os.path.normpath(launch_path), 'w')
            f.write(" ".join(args))
            f.close()

            # Run the test as an independant process
            try:
                p = subprocess.Popen(args)
                self.tpid = p.pid
                TaskMngr.addChildPid(pid=p.pid)
            except Exception as e:
                self.error('unable to fork te: %s' % e)
                self.setState(state=STATE_ERROR)
            else:
                p.wait()
                retcode = p.returncode

                if retcode == 0:
                    self.setState(state=STATE_COMPLETE)
                else:
                    self.error(
                        'unknown return code received: %s' %
                        str(retcode))
                    self.setState(state=STATE_ERROR)

            if self.tpid is not None:
                TaskMngr.delChildPid(pid=p.pid)

            # notify result log and by mail
            self.notifResultLog()
            if not self.withoutNotif:
                self.notifResultMail()

            if not self.noKeepTr:
                # Inspect the data storage adapters, zip data if present
                # and notify users connected
                zipped = StorageDataAdapters.instance().zipDataV2(
                    dirToday=time.strftime(
                        "%Y-%m-%d",
                        time.localtime(
                            self.prepareTime)),
                    dirTest=self.completeId(),
                    destPathZip=self.getPath(),
                    replayId=str(self.replayId),
                    projectId=self.projectId,
                    virtualName=self.completeId(withTestPath=True)
                )

                if zipped:
                    self.trace('Storage zipped for task')

            # delete temp adapters data storage
            StorageDataAdapters.instance().removeSubStorage(
                projectId=self.projectId,
                dirToday=time.strftime(
                    "%Y-%m-%d",
                    time.localtime(
                        self.prepareTime)),
                dirTest=self.completeId()
            )

            return self.state

    def notifResultMail(self):
        """
        """
        if platform.system() == "Windows":
            self.trace('Emails notifications not supported on Windows')
            return

        if not Settings.getInt('Notifications', 'emails'):
            self.trace('Emails notifications disabled')
            return

        if self.userName not in UsersManager.instance().cache():
            self.error('User=%s not found, send mail failed' % self.userName)
        else:
            user_profile = UsersManager.instance().cache()[self.userName]

            # PASS,FAIL,UNDEF,COMPLETE,ERROR,KILLED,CANCELLED
            # false;false;false;false;false;false;false
            notifications = user_profile['notifications'].split(';')
            self.trace(
                'notifications options for user %s: %s' %
                (self.userName, notifications))
            #  ['true', 'false', 'false', 'false', 'false', 'false', 'false', '']
            notifs_bool = [eval(x.title()) for x in notifications[:-1]]

            if self.state == STATE_COMPLETE and notifs_bool[3]:
                self.trace(
                    'task state [%s]: user notify enabled, mail to sent' %
                    self.state)
                self.sendMail(userTo=user_profile['email'])
            elif self.state == STATE_ERROR and notifs_bool[4]:
                self.trace(
                    'task state [%s]: user notify enabled, mail to sent' %
                    self.state)
                self.sendMail(userTo=user_profile['email'])
            elif self.state == STATE_KILLED and notifs_bool[5]:
                self.trace(
                    'task state [%s]: user notify enabled, mail to sent' %
                    self.state)
                self.sendMail(userTo=user_profile['email'])
            elif self.state == STATE_CANCELLED and notifs_bool[6]:
                self.trace(
                    'task state [%s]: user notify enabled, mail to sent' %
                    self.state)
                self.sendMail(userTo=user_profile['email'])
            else:
                self.trace(
                    'task state: notify on this state denied: %s' %
                    self.state)


import os, sys

###########

def verbose_print(msg):
	print msg

###########

def linetrim(s):
	return s.replace('\r', '').replace('\n','')
###

def sublast(s, s2):
	return s[s.find(s2)+len(s2):]

def parse_package(fpath):
	#p = fpath[:fpath.rfind('/')]
	p = fpath
	package = 'konoha'
	if p.find('/konoha/') != -1:
		return 'konoha'
	elif p.find('/class/') != -1:
		package = sublast(p, '/class/')
	elif p.find('/package/') != -1:
		package = '+' + sublast(p, '/package/')
	elif p.find('/api/') != -1:
		package = sublast(p, '/api/')
	elif p.find('/driver/') != -1:
		package = '#' + sublast(p, '/driver/')
	if package.find('_.') > 0: return 'konoha'
	if package.find('/') > 0:
		return package.split('/')[0]
	return package
#	p = fpath.split('/')
#	if p[-1].find('.') == -1: return p[-1]
#	return p[-2]
###

def fpath_filename(fpath):
	p = fpath.split('/')
	return p[-1].replace('.c', '')
###

def safedict(d, key, defv):
	if d.has_key(key): return d[key]
	d[key] = defv
	return defv
###

###

def list_topair(list):
	t1 = list[0]
	t2 = list[1]
	return t1, t2, list[2:]

def parse_funcparams(functype):
	if not functype.endswith(')'):
		debug_print('Invalid functype: %s' % functype)
	t = functype.replace('(', ' ').replace(',', ' ').replace(')', '').split()
	params = []
	while len(t) > 1:
		tt, tn, t = list_topair(t)
		params.append(nz_cparam(tt, tn))
	return params

###########

# ---------------------------------------------------------------------------

LINE = '''
/* ------------------------------------------------------------------------ */
'''

DLINE = '''
/* ======================================================================== */
'''

# ---------------------------------------------------------------------------

def write_println(f, msg = ''):
	f.write(msg+'\n')

def write_line(f):
	f.write(LINE)

def write_dline(f):
	f.write(DLINE)

def write_comment(f, msg):
	f.write('/* %s */\n' % msg)

def write_chapter(f, msg):
	f.write(DLINE)
	write_comment(f, msg)

def write_section(f, msg):
	f.write(LINE)
	write_comment(f, msg)

def write_define(f, name, value='', n=40):
	s = '#define %s ' % name
	while(len(s) < n) : s+=' '
	f.write(s)
	f.write(value)
	f.write('\n')
###

def write_ifndefine(f, name, value='', n=40):
	f.write('#ifndef %s\n' % name)
	write_define(f, name, value, n)
	f.write('#endif\n')
###

def write_ifndef(f, name, value='', n=40):
	f.write('#ifndef %s\n' % name)
	write_define(f, name, value, n)
	f.write('#endif\n')

def write_ifdef(f, n):
	f.write('''
#ifdef  %s''' % n.upper())

def write_else(f, n):
	f.write('''
#else /*%s*/
''' % n.upper())

def write_endif(f, n):
	f.write('''
#endif/*%s*/
''' % n.upper())

# ---------------------------------------------------------------------------

def write_BOM(f):
	f.write("%c%c%c" % (0xef, 0xbb, 0xbf))

def write_license(f):
	f.write('''/****************************************************************************
 * KONOHA COPYRIGHT, LICENSE NOTICE, AND DISCRIMER
 *
 * Copyright (c) 2005-2009, Kimio Kuramitsu <kimio at ynu.ac.jp>
 *           (c) 2008-      Konoha Software Foundation
 * All rights reserved.
 *
 * You may choose one of the following two licenses when you use konoha.
 * See www.konohaware.org/license.html for further information.
 *
 * (1) GNU General Public License 2.0      (with    KONOHA_UNDER_GPL2)
 * (2) Konoha Software Foundation License 1.0
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/
''')

def write_begin_c(f):
	f.write('''
#ifdef __cplusplus
extern "C" {
#endif
''')

def write_end_c(f):
	f.write('''
#ifdef __cplusplus
}
#endif
''')

# ---------------------------------------------------------------------------

def getdict(d, n, defv):
	if d.has_key(n): return d[n]
	return defv

def read_settings(fn):
	KNH_DATA = {}
	try:
		f = open(fn)
		exec(f)
		f.close()
		return KNH_DATA
	except OSError, e:
		print e
		return KNH_DATA

# ---------------------------------------------------------------------------

def nz_fname(fname):
	if fname.rfind('/') > 0: return fname[fname.rfind('/')+1:]
	return fname

def open_h(fname, lists):
	f = open(fname, 'w')
	write_license(f)
	d = nz_fname(fname).replace('.', '_'). upper()
	f.write('''
#ifndef %s
#define %s
''' % (d, d))

	for i in lists:
		f.write('''
#include%s''' % i)
	if len(lists) > 0: f.write('\n\n')
	write_begin_c(f)
	write_dline(f)
	return f

def open_h2(fname, lists):
	f = open(fname, 'w')
	write_license(f)
	d = nz_fname(fname).replace('.', '_'). upper()
	f.write('''
#ifndef %s
#define %s
''' % (d, d))

	for i in lists:
		f.write('''
#include%s''' % i)
	if len(lists) > 0: f.write('\n\n')
	return f

# ---------------------------------------------------------------------------

def close_h(f, fname):
	d = nz_fname(fname).replace('.', '_'). upper()
	write_end_c(f)
	write_dline(f)
	f.write('''
#endif/*%s*/
''' % d)
	f.close()

# ---------------------------------------------------------------------------

def open_c(fname, lists, bom = None):
	f = open(fname, 'w')
	if bom == 'BOM': write_BOM(f)
	write_license(f)
	for i in lists:
		f.write('''
#include%s''' % i)
	if len(lists) > 0: f.write('\n\n')
	write_begin_c(f)
	write_dline(f)
	return f

def close_c(f, fname):
	write_end_c(f)
	f.close()

def get_serial_number():
	f = open('SERIAL_NUMBER')
	n = int(f.readline())
	f.close()
	n += 1
	f = open('SERIAL_NUMBER', 'w')
	f.write('%d\n' % n)
	f.close()
	return n

# ---------------------------------------------------------------------------
# ---------------------------------------------------------------------------

def parse_options(option):
	d = {}
	if option is None: return d
	for t in option.split():
		if t.find('(') > 0:
			t = t.replace('(', ' ').replace(')', '')
			t = t.split()
			d[t[0]] = t[1]
		else:
			d[t] = 1
	return d

# ---------------------------------------------------------------------------

def check_ifdef(d):
	ifdef = ''
	endif = ''
	if d.has_key('@ifdef'):
		ifdef = '#ifdef  KNH_IMPORT_%s_\n' % d['@ifdef']
		endif = '#endif/*KNH_IMPORT_%s_*/\n' %d['@ifdef']
	return ifdef, endif

# ---------------------------------------------------------------------------

def alias_lname(cname):
	if cname.find('_') > 0:
		return cname.split('_')[1]
	return cname

def STRUCT_cname(cname):
	return 'STRUCT_%s' % cname

def STRUCT_sname(cname):
	return 'STRUCT_%s' % cname

def SAFE_cname(t) :
	t = t.replace('..', '')
	t = t.replace('!', '')
	t = t.replace('[]', '')
	t = t.replace('::', '__')
	t = t.replace(':', '__')
	return t

def CLASS_cname(cname) :
	prefix = ''
	if cname.endswith('[]'): prefix = 'A'
	if cname.endswith('..'): prefix = 'I'
	return '%sCLASS_%s' % (prefix, SAFE_cname(cname))

def TYPE_cname(t) :
	prefix = ''
	if t.endswith("[]!"): prefix = 'NNA'
	elif t.endswith("!") : prefix = 'NN'
	if t.endswith('[]'): prefix = 'A'
	if t.endswith('..'): prefix = 'NNI'
	return '%sTYPE_%s' % (prefix, SAFE_cname(t))

def DEBUG_cname(cname):
	return 'DEBUG_%s' % cname

def FIELDN_name(fn):
    return 'FIELDN_%s' % fn

def SAFE_mname(mname):
	return mname.replace('::', '__').replace(':', '__').replace('%', '_')

def METHODN_mname(mname):