from pathlib import Path
from datetime import datetime, timedelta
import sys
import numpy as np

class GMinuteData:
    
    def __init__(self, file = None, data = None):
        
        self._mnFmt = self._loadFmt(Path(sys.argv[0]).parent / 'mn_fmt.txt')
        self._mnr2Fmt = self._loadFmt(Path(sys.argv[0]).parent / 'mnr2_fmt.txt')
        self._success = False
        
        if file is not None:
            mn, mnr2 = self.read(file)
            self._mn = self.transform(mn, self._mnFmt)
            self._mnr2 = self.transform(mnr2, self._mnr2Fmt)
            if self.check():
                self.analysis()
                self._success = True
            
        elif data is not None:
            # assign from data
            self._mn, self._mnr2 = data
        else:
            # empty array
            self._mn, self._mnr2 = None, None
    
    # transfrom 24:00 => 00:00
    def transformTime(self, s):
        if len(s) != 12:
            return False
        if (s[8:10] == '24'):
            d = (datetime(int(s[0:4]), int(s[4:6]), int(s[6:8]), 0, int(s[10:12])) +
                timedelta(days = 1))
        else:
            d = datetime(int(s[0:4]), int(s[4:6]), int(s[6:8]), int(s[8:10]), int(s[10:12]))
        return np.datetime64(d, 'm')
    
    # transform value to float or nan
    def transformFloat(self, s):
        try:
            return float(s)
        except:
            return np.nan
    
    # read data from file.
    # @return mn (list), mnr2 (list)
    def read(self, file):
        with open(file, 'rt') as ptrIn:
            mn = []
            mnr2 = []
            for line in ptrIn:
                data = line.strip().split(',')
                if len(data) <= 4:
                    continue
                if data[2] == 'MN':
                    if len(data) < 63:
                        print('data lens error(', len(data),')', file, ':', line)
                        continue
                    mn.append(data[1:62])
                    continue
                if data[2] == 'MNR2':
                    # check data length
                    if len(data) < 10:
                        print('data lens error(', len(data),')', file, ':', line)
                        continue
                    mnr2.append(data[1:9])
                    continue
        return mn, mnr2
    
    def transform(self, data, fmt):
        # create columns in return
        result = {}
        for f in fmt:
            result[f['name']] = []
        
        for line in data:
            for d, f in zip(line, fmt):
                if f['name'] == 'DTIME':
                    result[f['name']].append(self.transformTime(d))
                elif f['type'] == 'SI':
                    result[f['name']].append(self.transformFloat(d))
                else:
                    result[f['name']].append(d)
        
        # transform to ndarray
        for f in fmt:
            result[f['name']] = np.array(result[f['name']])
        return result

    # write to file
    def write(self, file):
        with open(file, 'wt') as ptr:
            for i in range(len(self._mn['DTIME'])):
                outData = []
                for f in self._mnFmt:
                    if f['name'] == 'DTIME':
                        outData.append(str(self._mn[f['name']][i].astype('O')))
                    else:
                        outData.append(str(self._mn[f['name']][i]))
                for f in self._mnr2Fmt[3:]: # remove repeat columns
                    outData.append(str(self._mnr2[f['name']][i]))
                ptr.write(','.join(outData) + '\n')
    
    # analysis error value.
    def analysis(self):
        wd = self._mn['DD_10D']
        wd[wd == 99] = np.nan
    
    # check data after read.
    def check(self):
        if not np.all(self._mn['DTIME'] == self._mnr2['DTIME']):
            print('check MN and MNR2 time not match', file)
            return False
        return True
    
    def _loadFmt(self, path):
        fmt = []
        with open(path, 'rt', encoding='utf-8') as ptr:
            for line in ptr:
                cols = line.strip().split(',')
                if len(cols) >= 8:
                    fmt.append({
                        'col' : cols[0],
                        'name' : cols[1],
                        'type' : cols[2],
                        'len' : cols[3],
                        'point' : cols[4],
                        'show' : cols[5],
                        'unit' : cols[6],
                        'comment' : cols[7],
                    })
        return fmt
    
    def toHour(self):
        hours = m._mn['DTIME'].astype('datetime64[h]')
        distinctHours = np.sort(np.unique(hours))
        
        sumColumn = ['DD_Rmn', 'DD_RAD']
        maxColumn = ['DD_XXD', 'DD_FXX', 'DD_RX10_2', 'DD_RX60_2', 'DD_TR_2']
        
        hrmn = {}
        for f in self._mnFmt:
            hrmn[f['name']] = []
        
        for hr in distinctHours:
            for f in self._mnFmt:
                hrData = self._mn[f['name']][hours == hr]
                if f['name'] == 'DTIME':
                    hrmn[f['name']].append(hr)
                elif f['name'] == 'DD_F10':
                    pass # caculate by u,v
                elif f['name'] == 'DD_10D':
                    pass # caculate by u,v
                elif f['type'] == 'SI':
                    if f['name'] in sumColumn:
                        hrmn[f['name']].append(np.nansum(hrData))
                    elif f['name'] in maxColumn:
                        hrmn[f['name']].append(np.nanmax(hrData))
                    else:
                        hrmn[f['name']].append(np.nanmean(hrData))
                else:
                    hrmn[f['name']].append(hrData[0])
            
            ws = self._mn['DD_F10'][hours == hr]
            wd = self._mn['DD_10D'][hours == hr]
            u = np.sin(wd * (10. / 180. * np.pi)) * ws
            v = np.cos(wd * (10. / 180. * np.pi)) * ws
            meanU = np.nanmean(u)
            meanV = np.nanmean(v)
            meanWd = ((np.pi / 2. - np.arctan2(meanV, meanU)) / np.pi * 18) % 36
            meanWs = np.sqrt((meanU * meanU) + (meanV * meanV))
            if meanWd == 0: meanWd = 36
            if meanWs == 0: meanWd = 0
            hrmn['DD_F10'].append(meanWs)
            hrmn['DD_10D'].append(meanWd)
        
        hrmnr2 = {}
        for f in self._mnr2Fmt:
            hrmnr2[f['name']] = []
        
        for hr in distinctHours:
            for f in self._mnr2Fmt:
                hrData = self._mnr2[f['name']][hours == hr]
                if f['name'] == 'DTIME':
                    hrmnr2[f['name']].append(hr)
                elif f['type'] == 'SI':
                    if f['name'] in sumColumn:
                        hrmnr2[f['name']].append(np.nansum(hrData))
                    elif f['name'] in maxColumn:
                        hrmnr2[f['name']].append(np.nanmax(hrData))
                    else:
                        hrmnr2[f['name']].append(np.nanmean(hrData))
                else:
                    hrmnr2[f['name']].append(hrData[0])
        return GMinuteData(data = (hrmn, hrmnr2))
    
# find all file in paths
def globfiles(paths):
    files = []
    for path in map(Path, paths):
        if path.is_file():
            files.append(path)
        else:
            for file in path.glob('**/*.txt'):
                if file.is_file():
                    files.append(file)
    return files

if len(sys.argv) > 1:
    files = globfiles(sys.argv[1:])
    for file in files:
        print("process", file)
        m = GMinuteData(file)
        m.write(file.with_suffix('.csv'))
        h = m.toHour()
        h.write(file.parent / ('hour_' + file.stem  + '.csv'))
    input("transform finish. Push 'ENTER KEY' to continue ......")
else:
    input("Drop files or directory to icon. Push 'ENTER KEY' to continue ......")

file = Path(sys.argv[1])


