"""_____________________________________________________________________

:PROJECT: LARA

*layout_reader library*

:details: layout reader.
          -handling barcode intervals of non numeric 
            TA1001-TA1004 -> TA1001 TA1002 TA1003 TA1004
            OR01 - OR34   -> OR01, OR02, ..., OR34
         - 

:file:    admin.py
:authors: mark doerr

:date: (creation)          20190117
:date: (last modification) 20190119

.. note:: -
.. todo:: - 
________________________________________________________________________
"""
__version__ = "0.0.2"

import sys

import re
from io import StringIO
import logging
import laralib.container.barcode_handler as bh

import pandas as pd

class LayoutReader():
    """ Class doc """
    def __init__ (self):
        """ Class initialiser """
        
        self.layout_description = ""

    def load_layout(self, layout_filename):
        """ :param [param_name]: [description]"""
        
        with open(layout_filename, "r") as layout_file:
            self.plate_layout = layout_file.read()
            self.parseBarcode()
            self.parseDescription()
            self.parsePlateGeometry()
            self.parseUnits()
            self.parseLayoutDescriptions()
            self.parseVolumes()
            self.parseAdditions()
            
    def parseBarcode(self):
        """trying to find a "Barcode: " string and extract the barcodes
           :param -: - """
        try:
            self.barcode_seq = re.search(r'(\s*#*\s*[B,b]arcodes\s*:\s*)(?P<barcodes>.*)', 
                                    self.plate_layout).group('barcodes')
        except AttributeError as err:
            logging.error("wrong barcode format: {}".format(err) )
            
        logging.debug("barcodes:{}".format(self.barcode_seq) )
        
        #~ self.barcodes = bh.expandBCsequence(barcode)
        #~ bc = re.split('\s*,\s*', m.group('barcodes'))
        
    def parseDescription(self):
        """ 
        :param [param_name]: [description]"""
        try:
            self.layout_description = re.search(r'(\s*#*\s*[D,d]escription\s*:\s*)(?P<description>[\w\s]+)(.*)', 
                                                self.plate_layout).group('description')
                                                
            logging.debug("self.layout_description b {}".format(self.layout_description) )
            
        except AttributeError as err:
            logging.error("wrong description format: {}".format(err) )
        
    def parsePlateGeometry(self):
        """
        # num_rows=8 ; num_columns=12, ,,,, 
        :param [param_name]: [description]"""
        
        try:
            match = re.search(r'(\s*#*\s*num_rows\s*=\s*)(?P<num_rows>\d+)(\s*;\s*num_columns\s*=\s*)(?P<num_columns>\d+)(.*)', 
                                            self.plate_layout) # .group('description')
            self.nrows = int(match.group('num_rows'))
            self.ncols = int(match.group('num_columns'))

        except AttributeError as err:
            logging.error("wrong geometry format: {}".format(err) )
        
    def parseUnits(self):
        """
          # concentration_unit: uM ; volume_unit : ul; possible units: uM mM  M uL mL L,
         :param [param_name]: [description]"""
        
        try:
            match = re.search(r'(\s*#*\s*concentration_unit\s*:\s*)(?P<conc_unit>M|uM|mM)\s*;\s*volume_unit\s*:\s*(?P<vol_unit>l|L|mL|ml|ul|uL)(.*)', 
                                            self.plate_layout)
            #~ print(match.groups())
            
            self.conc_unit = match.group('conc_unit')
            self.vol_unit = match.group('vol_unit')
            
        except AttributeError as err:
            logging.error("wrong unit format: {}".format(err) )
    
    def parseLayoutDescriptions(self):
        """ :param [param_name]: [description]"""        

        # reading nrows, starting from "HEAD_DESCR" into pandas data frame
        f = StringIO(self.plate_layout[self.plate_layout.index('HEAD_DESCR'):])
        self.descr_df = pd.read_csv(f, nrows=self.nrows, index_col=0, comment="#", engine="c")
        
        print(self.descr_df)
        
        #~ transf_df = pd.melt(self.descr_df, value_vars=['A','B','C'])
        
        self.layout_df = self.descr_df.stack().reset_index()
        
        temp_df = self.layout_df[0].str.split(":",n=1, expand= True)
        
        self.layout_df['type'] = temp_df[0]
        self.layout_df['description'] = temp_df[1]
        self.layout_df.drop(num_columns =[0], inplace = True)
        
        self.layout_df.rename(num_columns = {'HEAD_DESCR':'row', 'level_1':'col'}, inplace = True)
        self.layout_df['well'] = self.layout_df['row'] + self.layout_df['col'].str.zfill(2)
        
        
        
        #~ df_res = pd.concat([df_st_ri, spl_df], axis=1)

    def parseVolumes(self):
        """
          # Volumes : 211,,,,,,,,,,
         :param [param_name]: [description]"""
        
        try:
            self.volumes = int( re.search(r'(\s*#*\s*[V,v]olumes\s*:\s*)(?P<volumes>\d+)(.*)', 
                                             self.plate_layout).group('volumes'))
            self.layout_df['volumes'] = self.volumes
             
        except AttributeError as err:
            logging.error("wrong volume format: {}".format(err) )
            
        
            
        #~ print(self.layout_df)
        
    def parseAdditions(self):
        """ :param [param_name]: [description]"""
        logging.warning("Additions impl. missing {}".format(1) )


def main():
    """
    main function
    """
    # command line arguments parser

    logging.basicConfig(level=logging.DEBUG)

    import argparse
    parser = argparse.ArgumentParser(description="load projects from YAML file")
    parser.add_argument("-f", "--file", type=str, help="YAML file with projects", default="projects.yaml")
    args = parser.parse_args()

    lor = LayoutReader()
    layout = lor.load_layout(args.file)

    ## pretty print projects

    print("------------- layout ---------------")
    logging.debug("barcodes:{}".format(lor.barcode_seq) )
    # conc unit
    logging.debug("conc_unit:{}".format(lor.conc_unit) )
    # vol unit
    logging.debug("vol_unit:{}".format(lor.vol_unit) )

    print("layout description:{}".format(lor.descr_df) )

    print("layout df:\n", lor.layout_df)


if __name__ == "__main__":
    sys.exit(main())