from reflex import ReflexIOParser, RecipeParameter, FitsFile
import sys


_ptypes = dict(file=str, list=str, string=str,
               float=float, int=int,
               bool=lambda x: True if x.lower() == 'true' else False)

class VisirParameter:

    def __init__(self, recipe, name, type,
                 description="", display_name=None,
                 options=[]):
        if type not in _ptypes:
            raise ValueError('%s is invalid type' % type)
        if type == 'list' and not options:
            raise ValueError('List parameter needs set of options')
        self.recipe = recipe
        self.display_name = name if not display_name else display_name
        self.name = name
        self.type = type
        self.description = description
        self.options = options

    @staticmethod
    def to_native(ptype, value):
        if not isinstance(value, basestring):
            return value
        if ptype in _ptypes:
            return _ptypes[ptype](value)
        return str(value)

    @staticmethod
    def to_string(ptype, value):
        if ptype == 'bool':
            return 'TRUE' if value else 'FALSE'
        return str(value)

def parseSop(sop):
    """
    remove when in reflex.py
    """
    retVal = list()
    sop_splitted = sop.split(',')
    for parameter in sop_splitted:
        param_recipe = parameter.partition('=')[0].partition(':')[0]
        param_name = parameter.partition('=')[0].partition(':')[2]
        param_value = parameter.partition('=')[2]
        p = RecipeParameter(param_recipe, param_name, "", "")
        p.value = param_value
        retVal.append(p)
    return retVal


try:
    from reflex import parseSof
except:
    # copy to avoid hard pyfits dependency
    def parseSof(sof):
        """
        parses the SoF passed by the PythonActor to 
        a list of FitsFile objects
        """
        retVal = list()
        dataset = sof[:sof.index('|')]
        only_sof = sof[sof.index('|')+1:]
        if len(only_sof) == 0 : 
          return retVal, dataset
        sof_splitted = only_sof.split(',')
        for frame in sof_splitted:
            name     = frame[0:frame.find(";")]
            category = frame[(frame.find(";")+1):(frame.rfind(";"))]
            purpose  = frame[(frame.rfind(";"))+1:]
            retVal.append(FitsFile(name, category, purpose))
        return retVal, dataset


def serialize_par(par):
    return "%s:%s=%s" % (par.recipe, par.name, str(par.value))


def join_sop(parlist):
    return ",".join((serialize_par(p) for p in parlist))


def serialize_fits(fits):
    return "%s;%s;%s" % (fits.name, fits.category, "")#fits.purpose)


def join_sof(fitslist, dataset):
    s = dataset + "|"
    s += ",".join((serialize_fits(f) for f in fitslist))
    return s


class ReflexLoopIO(ReflexIOParser):
    CONTINUE = 1
    REPEAT = 2
    UNDEFINED = 3

    def __init__(self):
        ReflexIOParser.__init__(self)
        # define inputs (Note: you must define at least long option)
        self.add_option("-i", "--in_sof",
                        dest="in_sof", type='string')
        self.add_option("-p", "--in_sop",
                        dest="in_sop", type='string')
        self.add_option("-g", "--in_sof_rec_orig",
                        dest="in_sof_rec_orig", type='string')
        self.add_option("-e", "--enable",
                        dest="enable", default='false')
        # define outputs (Note: you must define at least long option)
        self.add_output("-o", "--out_sof", dest="out_sof")
        self.add_output("-q", "--out_sop", dest="out_sop")
        self.add_output("-f", "--out_sof_loop", dest="out_sof_loop")
        self.add_output("-l", "--out_sop_loop", dest="out_sop_loop")
        self.add_output("-c", "--iteration_complete",
                        dest="iteration_complete")
        self.add_output("-s", "--set_enable",
                        dest="set_enable")

        self.enabled = True
        self.set_enable = None
        self.out_fits = []
        self.out_pars = []
        self.mode = self.UNDEFINED

    def parse_args(self):
        #get inputs from the command line
        (inputs, args) = ReflexIOParser.parse_args(self)

        #Input validation
        if not (inputs.in_sof and inputs.in_sop and
                inputs.in_sof_rec_orig):
            self.error("Options --in_sof, --in_sop and --in_sof_rec_orig are needed")
        if inputs.enable != 'true' and inputs.enable != 'false':
            self.error("Invalid --enable option. Use true or false")

        if inputs.enable == 'true':
            self.enabled = True
        elif inputs.enable == 'false':
            self.enabled = False
        self.in_fits, self.dataset = parseSof(inputs.in_sof)
        self.in_orig_fits, self.dataset = parseSof(inputs.in_sof_rec_orig)
        self.in_pars = parseSop(inputs.in_sop)
        self.all_fits = self.in_fits + self.in_orig_fits

    def passProductsThrough(self):
        self.out_fits = self.in_fits
        self.out_pars = self.in_pars
        self.mode = self.CONTINUE

    def set_continue(self):
        self.passProductsThrough()

    def set_repeat(self, mod_params):
        self.out_fits = self.in_orig_fits

        self.out_pars = []
        for p in self.in_pars:
            if p.name in mod_params:
                p.value = mod_params[p.name]
            self.out_pars.append(p)
        self.mode = self.REPEAT

    def print_outputs(self):
        if self.mode == self.CONTINUE:
            sof_prefix = 'ReflexParameter out_sof'
            sop_prefix = 'ReflexParameter out_sop'
            it_compl = 'true'
        elif self.mode == self.REPEAT:
            sof_prefix = 'ReflexParameter out_sof_loop'
            sop_prefix = 'ReflexParameter out_sop_loop'
            it_compl = 'false'
        else:
            # original reflex outputs nothing to stdout by default
            return

        if self.values.stdout_file is None:
            out = sys.stdout
        else:
            out = open(self.values.stdout_file, 'w')

        out.write('%s "%s"\n' % (sof_prefix, join_sof(self.out_fits, self.dataset)))
        out.write('%s "%s"\n' % (sop_prefix, join_sop(self.out_pars)))
        out.write('ReflexParameter iteration_complete "%s"\n' % it_compl)

        if self.set_enable is not None:
            out.write('ReflexParameter set_next_iteration_enable "false"\n')

        out.flush()
        if out is not sys.stdout:
            out.close()
