import pandas as pd
import numpy as np
from datetime import datetime
from datetime import timedelta

def get_bms_drive_timetable(df_bms,battery_mode):
    '''对df_bms进行处理,得到行车的时间表。'''

    #####################step1 先使用电流做充电状态的判断#############################################
    if battery_mode==0:#mode=0,电流为正代表放电
        condition_chrg=df_bms['bmspackcrnt']<0##根据电流,挑选充电状态
        df_bms.loc[condition_chrg,'bscsta']='chrg'
        condition_drive=df_bms['bmspackcrnt']>0.01##根据电流,挑选行驶状态
        df_bms.loc[condition_drive,'bscsta']='drive'
        df_bms.loc[~(condition_drive|condition_chrg),'bscsta']='idle'#静置状态
    else :#mode=1,电流为负代表放电
        condition_chrg=df_bms['bmspackcrnt']>0##根据电流,挑选充电状态
        df_bms.loc[condition_chrg,'bscsta']='chrg'
        condition_drive=df_bms['bmspackcrnt']<-0.01##根据电流,挑选行驶状态
        df_bms.loc[condition_drive,'bscsta']='drive'
        df_bms.loc[~(condition_drive|condition_chrg),'bscsta']='idle'#静置状态

    #####################step2 对drive进行debounce,进入时立即进入,退出时debounce,5分钟。##########
    index=0
    debounce_row=10#debounce判断持续10行
    debounce_time=300#debounce判断持续300秒
    #对上一步初步状态进行二次处理
    while index<(len(df_bms)-debounce_row):
        mode_0=df_bms.loc[index,'bscsta']
        mode_1=df_bms.loc[index+1,'bscsta']
        #如果发现了边界行,则进行debounce判断
        if (mode_0=='drive')&(mode_1!='drive'):#如果从drive变为idle
            accum_subtime=0#累计时间初始化

            for sub_index in range(debounce_row):#往下处理10行
                sub_time=df_bms.loc[index+sub_index,'deltatime']
                accum_subtime+=sub_time
                #如果累计时间不到300秒,则设置为drive
                if accum_subtime<debounce_time:
                    df_bms.loc[index+sub_index,'bscsta']='drive'
            index=index+debounce_row#处理10行以后的数据
        #如果从idle变为drivemode,则将idle变为drive,包容前一行
        elif (mode_0!='drive')&(mode_1=='drive'): 
            df_bms.loc[index,'bscsta']='drive'
            index=index+1
        else:
            index=index+1


    #######################step3 对drivemode的时间进行分段###########################################
    not_drive_flg=0#初始化
    #输出drivemode的时间段,包含开始时间、结束时间
    df_bms_drive_timetable_index=0
    df_bms_drive_timetable=pd.DataFrame([],columns={'drive_start_time','drive_end_time',
                                                    'gps_dist','predict_dist','drive_start_soc','drive_end_soc'})
    for index in range(len(df_bms)):
        temp_bscsta=df_bms.loc[index,'bscsta']
        
        if (temp_bscsta=='drive')&(not_drive_flg==0):
            drive_start_time=df_bms.loc[index,'time']
            not_drive_flg=1
            df_bms_drive_timetable.loc[df_bms_drive_timetable_index,'drive_start_time']=drive_start_time
            #startsoc
            drive_start_soc=df_bms.loc[index,'bmspacksoc']
            df_bms_drive_timetable.loc[df_bms_drive_timetable_index,'drive_start_soc']=drive_start_soc

        elif (temp_bscsta!='drive')&(not_drive_flg==1):
            drive_end_time=df_bms.loc[index,'time']
            not_drive_flg=0
            df_bms_drive_timetable.loc[df_bms_drive_timetable_index,'drive_end_time']=drive_end_time
            #endsoc
            drive_end_soc=df_bms.loc[index,'bmspacksoc']
            df_bms_drive_timetable.loc[df_bms_drive_timetable_index,'drive_end_soc']=drive_end_soc
            df_bms_drive_timetable_index+=1#index++

    #删除时间信息不齐全的行
    df_bms_drive_timetable=df_bms_drive_timetable.dropna(subset=['drive_end_time','drive_start_time'])
    
    return df_bms_drive_timetable


def read_df_bms(path):
    '''从路径中读取df_bms,进行预处理'''
    df_bms=pd.read_csv(path, encoding='gbk')#编码方式gbk
    #筛选表头,重命名
    bms_columns=['时间戳','总电流[A]','总电压[V]','SOC[%]']
    df_bms=df_bms.loc[:,bms_columns].copy()
    df_bms.rename(columns = {"时间戳": "time", "总电流[A]": "bmspackcrnt", 
                             "总电压[V]": "bmspackvol", "SOC[%]": "bmspacksoc"},inplace=True)#表头替换
    #时间格式调整
    df_bms['time']=df_bms['time'].apply(lambda x:datetime.strptime(x,'%Y-%m-%d %H:%M:%S'))#时间格式调整
    #进行预处理
    df_bms=df_add_deltatime(df_bms)#增加deltatime列 
    return df_bms

def preprocess_Df_Bms(df_bms):
    '''对获得的df_bms,进行预处理'''
    #筛选表头,重命名
    bms_columns=['时间戳','总电流[A]','总电压[V]','SOC[%]']
    df_bms=df_bms.loc[:,bms_columns].copy()
    df_bms.rename(columns = {"时间戳": "time", "总电流[A]": "bmspackcrnt", 
                             "总电压[V]": "bmspackvol", "SOC[%]": "bmspacksoc"},inplace=True)#表头替换
    #删除空行
    df_bms=df_bms.dropna(subset=['time'])
    #删除时间重复的行,保留第一次出现的行
    df_bms=df_bms.drop_duplicates(subset=['time'],keep='first')
    #时间格式调整
    df_bms['time']=df_bms['time'].apply(lambda x:datetime.strptime(x,'%Y-%m-%d %H:%M:%S'))#时间格式调整
    #进行预处理
    df_bms=df_add_deltatime(df_bms)#增加deltatime列 
    return df_bms


def df_add_deltatime(df_in):
    '''Add a columns:deltatime,input df must have time column.'''
    for i in range(len(df_in)):
        #首行默认为0
        if i==0:
            df_in.loc[i,'deltatime']=0
        #从第二行开始,计算i行到i-1行,GPS距离之差
        else:
            time1=df_in.loc[i-1,'time']
            time2=df_in.loc[i,'time']
            deltatime=time_interval(time1,time2)#计算时间差,返回单位为秒。
            df_in.loc[i,'deltatime']=deltatime
    return df_in


def time_interval(time1,time2):
    """
    Calculate the time interval between two times,
    return the seconds
    """
    deltatime=time2-time1
    return deltatime.seconds


def cal_deltasoc(df_bms,start_time,end_time):
    '''输入开始时间和结束时间,返回deltasoc,此处将deltasoc*1既等效为unrecorded_odo.'''
    time_condition=(df_bms['time']>start_time)&(df_bms['time']<end_time)
    df_bms_sub=df_bms.loc[time_condition,:].copy()
    if len(df_bms_sub)>=2:
        
        df_bms_head=df_bms_sub.head(1).copy()#首行
        df_bms_startsoc=df_bms_head['bmspacksoc'].values[0]
        df_bms_tail=df_bms_sub.tail(1).copy()#尾行
        df_bms_endsoc=df_bms_tail['bmspacksoc'].values[0]
        delta_soc=df_bms_startsoc-df_bms_endsoc
        
        if delta_soc>0:
            #如果df_bms出现时间不连续,则先计算deltasoc,deltasoc每变化1,续驶里程增加1,
            unrecorded_odo=delta_soc*1
            #print('From '+str(start_time)+' to  '+str(end_time)+' soc decrease:  '+str(delta_soc))
        else:
            unrecorded_odo=0#如果deltasoc不大于0,说明在充电,或者静置不动    
    #如果行数少于2,无法计算
    else:
        unrecorded_odo=0
    return unrecorded_odo