import sys
import os
import re
import subprocess

# sys.path.append("modules")
try:
    from model import *
except:
    raise


class EnergyAnalyzer():
    '''
    Energy analyzer
    '''
    def __init__(self, productname,
                 isDebugging=False, unit="mW", output_path=None, logger=None):
        self.myModel = Model(isDebugging=isDebugging, unit=unit)
        self.myModel.load(productname)
        self.num_of_cores = getCoreNum(productname)
        # first time initialization
        self.clean_up_cpu()
        self.clean_up_net()
        # define output path, if not set then will not output to file
        self.output_path = output_path

        self.DEBUG = isDebugging
        if logger is None:
            self.logger = EmptyLogger(
                "EnergyAnalyzer", isDebugging=self.DEBUG, printout=True)
        else:
            self.logger = logger

    '''
    Variable clean up functions
    '''
    def clean_up_cpu(self):
        self.clean_up_cpu_data()
        self.clean_up_cpu_result()

    def clean_up_cpu_data(self):
        self.data_cpu = []  # (sorted) cpu results of logs
        self.data_cpu_details = []
        self.data_cpu_d = []  # deltas between pair of results
        self.data_cpu_details_d = []  # deltas between pair of results
        self.avg_log_freq = 0

    def clean_up_cpu_result(self):
        self.instant_freqs = []  # freq of each core at time unit
        self.instant_utils = []  # util perc of each core at time unit
        self.instant_power = []  # power spent at time unit
        self.cpu_idle = []  # [cpu0, cpu1, ..., cpuN, cpuOverall]
        self.cpu_used = []  # [cpu0, cpu1, ..., cpuN, cpuOverall]
        self.cpu_energy_total = 0  # unit as defined in unit
        self.cpu_power_avg = 0  # average power across time
        self.cpu_utils_avg = []  # [cpu0, cpu1, ..., cpuN, cpuOverall]
        self.cpu_time_total = 0

    def clean_up_net(self):
        self.clean_up_net_data()
        self.clean_up_wifi_result()
        self.clean_up_lte_result()

    def clean_up_net_data(self):
        self.data_tcpdump = []
        # [[time, net_state, data_len], ...]
        # net_state: 'i': 'idle', 'a': 'active', 't': 'tail'
        self.net_start_time = float('-inf')
        self.net_end_time = float('inf')
        self.data_size = 0
        self.wifi_avg_thrpt = 0
        # WiFi
        self.wifi_rssi = []
        # LTE
        self.lte_rsrp = []
        # GSM
        self.gsm_rssi = []

    def clean_up_wifi_result(self):
        self.wifi_instant_thrpt = []
        self.wifi_energy = 0
        self.wifi_time = 0
        self.wifi_power = 0
        self.wifi_active_time = 0
        self.wifi_tail_time = 0
        self.wifi_tail_energy = 0
        self.wifi_active_energy = 0

    def clean_up_lte_result(self):
        pass

    '''
    CPU
    '''
    def parse_cpu_raw(self, filepath, details=True):
        def get_user(tmp, offset):
            return int(tmp[1 + offset])

        def get_nice(tmp, offset):
            return int(tmp[2 + offset])

        def get_system(tmp, offset):
            return int(tmp[3 + offset])

        def get_idle(tmp, offset):
            return int(tmp[4 + offset])

        def get_iowait(tmp, offset):
            return int(tmp[5 + offset])

        def get_irq(tmp, offset):
            return int(tmp[6 + offset])

        def get_softirq(tmp, offset):
            return int(tmp[7 + offset])

        def get_freq(tmp, offset):
            return int(tmp[11 + offset])

        def get_busy(tmp, offset):
            return get_user(tmp, offset) + get_nice(tmp, offset) +\
                get_system(tmp, offset) + get_iowait(tmp, offset) +\
                get_irq(tmp, offset) + get_softirq(tmp, offset)

        if not os.path.isfile(filepath):
            self.logger.error(
                "cpu raw log file {0} does not exist".format(filepath))
            sys.exit(-1)

        self.logger.debug("parse_cpu_raw started")
        with open(filepath, 'rU') as f:
            contents = f.readlines()
        f = open(filepath[:-3], 'wb')
        if details:
            f2 = open(filepath[:-3] + 'Detail', 'wb')
        for line in contents:
            tmp = line.split()
            f.write("{0} {1} {2}".format(
                tmp[0], get_idle(tmp, 1), get_busy(tmp, 1)))
            if details:
                f2.write("{0}:{1} {2} {3} {4} {5} {6} {7}".format(
                    tmp[0], get_busy(tmp, 1) + get_idle(tmp, 1),
                    get_user(tmp, 1), get_nice(tmp, 1),
                    get_system(tmp, 1), get_iowait(tmp, 1),
                    get_irq(tmp, 1), get_softirq(tmp, 1)))
            for i in xrange(self.num_of_cores):
                offset = (i + 1) * 12
                f.write(" {0} {1} {2}".format(
                    get_idle(tmp, offset),
                    get_busy(tmp, offset),
                    get_freq(tmp, offset)))
                if details:
                    f2.write(":{0} {1} {2} {3} {4} {5} {6}".format(
                        get_busy(tmp, offset) + get_idle(tmp, offset),
                        get_user(tmp, offset), get_nice(tmp, offset),
                        get_system(tmp, offset), get_iowait(tmp, offset),
                        get_irq(tmp, offset), get_softirq(tmp, offset)))
            f.write('\n')
            if details:
                f2.write('\n')
        f.close()
        if details:
            f2.close()
        self.logger.debug("parse_cpu_raw ended")

    def read_cpu_log(self, filepath,
                     startT=float('-inf'), endT=float('inf'),
                     details=True):
        if '.cpuRaw' in filepath or not os.path.isfile(filepath):
            if '.cpuRaw' in filepath:
                self.parse_cpu_raw(filepath)
                filepath = filepath[:-3]
            else:
                self.parse_cpu_raw(filepath + 'Raw')
        if not os.path.isfile(filepath):
            self.logger.error(
                "cpu log file {0} does not exist".format(filepath))
            sys.exit(-1)
        self.logger.debug("clean up cpu data")
        self.clean_up_cpu_data()
        self.logger.debug("read_cpu_log started")
        contents = []
        skipFirstTime = True
        timeGap_sum_buff = 0
        timeGap = 0.1 * 2
        with open(filepath, 'rU') as f:
            contents = f.readlines()
        if details:
            with open(filepath + 'Detail', 'rU') as f:
                contents2 = f.readlines()
        for line_idx in xrange(len(contents)):
            line = contents[line_idx]
            line_details = contents2[line_idx]
            tmp = line.rstrip().split(' ')
            if details:
                tmp2 = line_details.rstrip().split(':')
                tmp2_cpu = tmp2[1].split(' ')
            if len(tmp) < 3:
                print "something is wrong at splitting the line for cpu_log"
                sys.exit(-1)
            timestamp = int(tmp[0]) / 1000.0  # ms -> s
            # self.logger.debug(
            #     "time: {0}, startT: {1}, endT: {2}".format(
            #         timestamp, startT, endT))
            if timestamp < (startT - timeGap) or timestamp > (endT + timeGap):
                continue
            timeGap_sum_buff += timeGap
            # self.logger.debug("passed")
            cpu_cpu_idle = int(tmp[1])
            cpu_cpu_used = int(tmp[2])
            cpu_per_core = []
            if details:
                cpu_cpu_user = int(tmp2_cpu[1])
                cpu_cpu_nice = int(tmp2_cpu[2])
                cpu_cpu_system = int(tmp2_cpu[3])
                cpu_cpu_iowait = int(tmp2_cpu[4])
                cpu_cpu_irq = int(tmp2_cpu[5])
                cpu_cpu_softirq = int(tmp2_cpu[6])
            if not skipFirstTime:
                delta_t = timestamp - self.data_cpu[-1][0]
                timeGap = delta_t * 2
                # self.logger.debug(timeGap)
                delta_cpu_idle = cpu_cpu_idle - self.data_cpu[-1][1]
                delta_cpu_used = cpu_cpu_used - self.data_cpu[-1][2]
                delta_per_core = []
                if details:
                    delta_cpu_user = \
                        cpu_cpu_user - self.data_cpu_details[-1][0]
                    delta_cpu_nice = \
                        cpu_cpu_nice - self.data_cpu_details[-1][1]
                    delta_cpu_system = \
                        cpu_cpu_system - self.data_cpu_details[-1][2]
                    delta_cpu_iowait = \
                        cpu_cpu_iowait - self.data_cpu_details[-1][3]
                    delta_cpu_irq = cpu_cpu_irq - self.data_cpu_details[-1][4]
                    delta_cpu_softirq = \
                        cpu_cpu_softirq - self.data_cpu_details[-1][5]
            for i in xrange(3, len(tmp), 3):
                cpu_i_idle = int(tmp[i])
                cpu_i_used = int(tmp[i + 1])
                cpu_i_freq = int(tmp[i + 2])
                cpu_per_core.append([cpu_i_idle, cpu_i_used, cpu_i_freq])
                if len(self.data_cpu) != 0:
                    delta_per_core.append(
                        [cpu_i_idle - self.data_cpu[-1][3][i / 3 - 1][0],
                         cpu_i_used - self.data_cpu[-1][3][i / 3 - 1][1],
                         cpu_i_freq])  # self.data_cpu[-1][3][i / 3 - 1][2])
            if not skipFirstTime:
                self.data_cpu_d.append(
                    [delta_t, delta_cpu_idle,
                     delta_cpu_used, delta_per_core])
                self.data_cpu_details_d.append(
                    [delta_cpu_user, delta_cpu_nice, delta_cpu_system,
                     delta_cpu_iowait, delta_cpu_irq, delta_cpu_softirq])
            self.data_cpu.append(
                [timestamp, cpu_cpu_idle, cpu_cpu_used, cpu_per_core])
            self.data_cpu_details.append(
                [cpu_cpu_user, cpu_cpu_nice, cpu_cpu_system,
                 cpu_cpu_iowait, cpu_cpu_irq, cpu_cpu_softirq])
            skipFirstTime = False
        if len(self.data_cpu_d) < 1:
            self.logger.error("parse_cpu_energy finds delta empty")
            self.logger.error(self.data_cpu)
            self.logger.error("check the file and see why")
        # calculate the logging frequency
        self.avg_log_freq = timeGap_sum_buff / 2 / len(self.data_cpu_d)
        self.logger.debug("read_cpu_log ended")

    def parse_cpu_energy(self,
                         power_base=0, details=True):
        self.logger.debug("clean up cpu result")
        self.clean_up_cpu_result()
        if len(self.data_cpu_d) < 1:
            self.logger.error("parse_cpu_energy finds delta empty")
            self.logger.error(self.data_cpu)
            self.logger.error("did you call read_cpu_log?")
            return
        self.logger.debug("parse_cpu_energy started")
        num_of_cores = len(self.data_cpu_d[0][3])
        # allocate memory
        self.cpu_idle = [0 for i in xrange(num_of_cores + 1)]
        self.cpu_used = [0 for i in xrange(num_of_cores + 1)]
        self.cpu_utils_avg = [0 for i in xrange(num_of_cores + 1)]
        if details:
            self.cpu_user_util = 0
            self.cpu_nice_util = 0
            self.cpu_system_util = 0
            self.cpu_iowait_util = 0
            self.cpu_irq_util = 0
            self.cpu_softirq_util = 0
        for result_idx in xrange(len(self.data_cpu_d)):
            result = self.data_cpu_d[result_idx]
            # allocate memory
            freqs = [0 for i in xrange(num_of_cores)]
            utils = [0 for i in xrange(num_of_cores + 1)]
            for i in xrange(num_of_cores):
                if result[3][i][0] + result[3][i][1] > 0:
                    utils[i] = 1.0 * \
                        result[3][i][1] / (result[3][i][0] + result[3][i][1])
                    self.cpu_idle[i] += result[3][i][0]
                    self.cpu_used[i] += result[3][i][1]
                else:
                    utils[i] = 0
                freqs[i] = result[3][i][-1]
            # calculate total
            if result[1] + result[2] > 0:
                utils[-1] = 1.0 * result[2] / (result[1] + result[2])
                self.cpu_idle[-1] += result[1]
                self.cpu_used[-1] += result[2]
            else:
                utils[-1] = 0
            if details:
                self.cpu_user_util += self.data_cpu_details_d[result_idx][0]
                self.cpu_nice_util += self.data_cpu_details_d[result_idx][1]
                self.cpu_system_util += self.data_cpu_details_d[result_idx][2]
                self.cpu_iowait_util += self.data_cpu_details_d[result_idx][3]
                self.cpu_irq_util += self.data_cpu_details_d[result_idx][4]
                self.cpu_softirq_util += self.data_cpu_details_d[result_idx][5]
            # store the results
            self.instant_freqs.append(freqs)
            self.instant_utils.append(utils)
            # get power (only if the unit is in Watts or mWatts)
            instant_power = self.myModel.get_cpu_energy(
                result[0], freqs, utils[:-1])
            # check if returned is power (watt) or energy (joule)
            if self.myModel.using_power:
                # subtract base (background) if desired
                instant_power -= power_base
                self.instant_power.append(instant_power)
                energy = instant_power * result[0]
            else:
                self.instant_power.append(
                    instant_power / result[0] - power_base)
                instant_power -= power_base * result[0]
                energy = instant_power
            self.cpu_time_total += result[0]
            self.cpu_energy_total += energy
        if details:
            self.cpu_user_util = 1.0 * \
                self.cpu_user_util / (self.cpu_used[-1] + self.cpu_idle[-1])
            self.cpu_nice_util = 1.0 * \
                self.cpu_nice_util / (self.cpu_used[-1] + self.cpu_idle[-1])
            self.cpu_system_util = 1.0 * \
                self.cpu_system_util / (self.cpu_used[-1] + self.cpu_idle[-1])
            self.cpu_iowait_util = 1.0 * \
                self.cpu_iowait_util / (self.cpu_used[-1] + self.cpu_idle[-1])
            self.cpu_irq_util = 1.0 * \
                self.cpu_irq_util / (self.cpu_used[-1] + self.cpu_idle[-1])
            self.cpu_softirq_util = 1.0 * \
                self.cpu_softirq_util / (self.cpu_used[-1] + self.cpu_idle[-1])
        self.cpu_power_avg = self.cpu_energy_total / self.cpu_time_total
        for i in xrange(num_of_cores + 1):
            self.cpu_utils_avg[i] = 1.0 * \
                self.cpu_used[i] / (self.cpu_used[i] + self.cpu_idle[i])
        # self.logger.debug(self.instant_power)
        self.logger.debug("parse_cpu_energy ended")

    '''
    Network
    '''
    def read_sslogger_file(self, fp_sslogger):
        if not os.path.isfile(fp_sslogger):
            self.logger.error(
                "sslogger file {0} does not exist".format(fp_sslogger))
            sys.exit(-1)
        myPatterns = [  # wifi
                      '(\d+) wifi ([-\d]+)',
                        # gsm, LTE
                      '(\d+) gsm ([-\d]+) lte ([-\d]+) ([-\d]+) ([-\d]+)']
        with open(fp_sslogger, 'rU') as f:
            content = f.readlines()
        for line in content:
            tmp = None
            for pattern in myPatterns:
                myMatch = re.search(pattern, line)
                if myMatch is not None:
                    # [timestamp (ms), wifi rssi]
                    # or
                    # [timestamp (ms), gsm rssi, lte ss, lte rsrp, lte rsrq]
                    tmp = myMatch.groups()
                    break
            if tmp is None:
                self.logger.debug("nothing found in {0}".format(line.rstrip()))
                continue
            if len(tmp) > 2:
                # [timestamp (ss), lte rsrp]
                self.lte_rsrp.append([int(tmp[0]) / 1000.0, int(tmp[3])])
                # [timestamp (ss), gsm rssi]
                self.gsm_rssi.append([int(tmp[0]) / 1000.0, int(tmp[1])])
            else:
                # [timestamp (s), wifi rssi]
                self.wifi_rssi.append([int(tmp[0]) / 1000.0, int(tmp[1])])
        # self.logger.debug(self.lte_rsrp)
        # self.logger.debug(self.wifi_rssi)

    def read_tcpdump_file(self, fp_tcpdump,
                          size_limit=None,
                          isWiFi=False, isLTE=False, is3G=False):
        # TODO: currently WIFi only
        if not os.path.isfile(fp_tcpdump):
            self.logger.error(
                "tcpdump file {0} does not exist".format(fp_tcpdump))
            sys.exit(-1)
        myPatterns = [  # DATA packet, with seq range
                      '([\d\.]+) IP ([\d\.]+) \> ([\d\.]+): ' +
                      'Flags \[([\w\.]+)\], seq (\d+):(\d+),',
                        # ACK
                      '([\d\.]+) IP ([\d\.]+) \> ([\d\.]+): ' +
                      'Flags \[([\w\.]+)\], ack (\d+),',
                        # SYN/SYNACK or FIN/FINACK packet, with one seq
                      '([\d\.]+) IP ([\d\.]+) \> ([\d\.]+): ' +
                      'Flags \[([\w\.]+)\], seq (\d+),',
                        # UDP
                      '([\d\.]+) IP ([\d\.]+) \> ([\d\.]+): ' +
                      'UDP, length (\d+)',
                        # offloading
                      '([\d\.]+) IP0 bad-hlen 0']
        with open(fp_tcpdump, 'rU') as f:
            content = f.readlines()
        firstLine = True
        total_bytes = 0
        # check the network status: idle, active, or tail
        net_state = 'a'  # active
        for line in content:
            tmp = None
            for pattern_i in xrange(len(myPatterns)):
                myMatch = re.search(myPatterns[pattern_i], line)
                if myMatch is not None:
                    # [timestamp (s), src_ip, dst_ip, flag, seq_start, seq_end]
                    # or
                    # [timestamp (s), src_ip, dst_ip, flag, seq]
                    tmp = list(myMatch.groups())
                    tmp[0] = float(tmp[0])  # timestamp
                    break
            if tmp is None:
                self.logger.debug("nothing found in {0}".format(line.rstrip()))
                continue
            # if pattern_i == 1:
            #     data_type = 'ack'
            # else:
            #     data_type = 'seq'
            data_len = 0
            if len(tmp) > 5 or pattern_i > 2:
                if pattern_i == 3:  # UDP
                    data_len = int(tmp[3])
                elif pattern_i == 4:  # offloading
                    data_len = 1488
                else:
                    data_len = int(tmp[5]) - int(tmp[4])
                total_bytes += data_len
                # self.logger.debug("{0}".format(total_bytes))
                if size_limit is not None and total_bytes > size_limit:
                    break
            if firstLine:
                firstLine = False
            elif self.data_tcpdump[-1][1] == 'i' \
                    or self.data_tcpdump[-1][1] == 't':
                net_state = 'a'  # active
            elif self.data_tcpdump[-1][1] == 'a':
                prevT = self.data_tcpdump[-1][0]
                time_diff = tmp[0] - prevT
                # self.logger.debug("time_diff: {0:.4f}".format(time_diff))
                if time_diff > self.myModel.wifi_min_spike_interval:
                    self.data_tcpdump[-1][1] = 't'  # prev is tail
                if time_diff > self.myModel.wifi_timeout:
                    self.data_tcpdump.append(
                        [prevT + self.myModel.wifi_timeout,
                         'i',
                         self.data_tcpdump[-1][2]])
            self.data_tcpdump.append(
                [tmp[0], net_state, data_len])
        # check result
        if len(self.data_tcpdump) < 2:
            self.logger.error("tcpdump file has weird size...")
            sys.exit(1)
        # the last one must be tail
        self.data_tcpdump[-1][1] = 't'
        # after self.myModel.wifi_timeout, it will become idle
        self.data_tcpdump.append(
            [self.data_tcpdump[-1][0] + self.myModel.wifi_timeout, 'i', 0])
        # self.logger.debug(self.data_tcpdump)
        # get the start and end time of network
        self.net_start_time = self.data_tcpdump[0][0]
        self.net_end_time = self.data_tcpdump[-2][0]  # true net end time
        self.data_size = total_bytes
        self.wifi_avg_thrpt = \
            total_bytes / (self.net_end_time - self.net_start_time)

    def read_wifi_log(self, fp_tcpdump, tcpdump_filter="",
                      size_limit=None,
                      fp_sslogger=None, delete_ori_tcpdump=True):
        self.logger.debug("clean up network data")
        self.clean_up_net_data()
        self.logger.debug("read_wifi_log started")
        # parse tcpdump file
        self.logger.debug("parse tcpdump")
        if ".tcpdump" not in fp_tcpdump:
            if not os.path.isfile("{0}.tcpdump".format(fp_tcpdump)):
                # check if raw file exists
                if not os.path.isfile(fp_tcpdump):
                    self.logger.error(
                        "tcpdump file {0} does not exist".format(fp_tcpdump))
                    sys.exit(-1)
                # generate the parsed tcpdump file
                subprocess.call(
                    "tcpdump -tt -n -r {0} {1} > {0}.tcpdump".format(
                        fp_tcpdump, tcpdump_filter),
                    shell=True)
                # remove the big file as it is too big
                if delete_ori_tcpdump:
                    subprocess.call("rm {0}".format(fp_tcpdump), shell=True)
            fp_tcpdump += '.tcpdump'
        # read the file
        self.read_tcpdump_file(fp_tcpdump, size_limit=size_limit, isWiFi=True)
        # parse sslogger
        self.logger.debug("parse sslogger file")
        if fp_sslogger is None:
            self.logger.info(
                "no sslogger file, will assume the strongest")
            return
        elif not os.path.isfile(fp_sslogger):
            self.logger.info(
                "sslogger file {0} does not exist".format(fp_sslogger))
            sys.exit(-1)
        self.read_sslogger_file(fp_sslogger)
        self.logger.debug("read_wifi_log ended")

    def parse_wifi_energy(self, power_base=0, isTX=True):
        self.logger.debug("clean up wifi result")
        self.clean_up_wifi_result()
        if len(self.data_tcpdump) < 2:
            self.logger.error("parse_wifi_energy finds data_tcpdump empty")
            self.logger.error(self.data_tcpdump)
            self.logger.error("did you call read_wifi_log?")
            return
        self.logger.debug("parse_wifi_energy started")
        # by default assume the rssi is the max
        curRSSI_max = sorted(
            self.myModel.net_wifi['active'].keys(), reverse=True)[0]
        curRSSI = curRSSI_max
        curRSSI_idx = None
        if len(self.wifi_rssi) > 0:
            curRSSI_idx = 0
            curRSSI = self.wifi_rssi[0][1]
        # for convenience the first throughput point is initially set to 0
        self.wifi_instant_thrpt.append(
            [self.data_tcpdump[0][0], 0])
        tmp_data_size = 0
        # derive energy for wifi network
        for i in xrange(len(self.data_tcpdump) - 1):
            # find my current rssi (only if sslogger has the data)
            if curRSSI_idx is not None and \
                    curRSSI_idx < len(self.wifi_rssi) - 1:
                while self.wifi_rssi[
                        curRSSI_idx + 1][0] < self.data_tcpdump[i][0]:
                    curRSSI_idx += 1
                    if curRSSI_idx >= len(self.wifi_rssi) - 1:
                        curRSSI_idx -= 1
                        break
                curRSSI = self.wifi_rssi[curRSSI_idx][1]
            # self.logger.debug("myRSSI: {0}dB".format(curRSSI))
            diffT = self.data_tcpdump[i + 1][0] - self.data_tcpdump[i][0]
            # self.logger.debug("diffT: {0:.8f}".format(diffT))
            # self.logger.debug("{0}".format(self.data_tcpdump[i]))
            if self.data_tcpdump[i][1] == 'a':
                curPower = self.myModel.get_wifi_active_energy(
                    diffT, curRSSI)
                # check if returned is power (watt) or energy (joule)
                if self.myModel.using_power:
                    # subtract base (background) if desired
                    curPower -= power_base
                    energy = curPower * diffT
                else:
                    curPower -= power_base * diffT
                    energy = curPower
                self.wifi_active_energy += energy
                self.wifi_active_time += diffT
                tmp_data_size += self.data_tcpdump[i][2]
            elif self.data_tcpdump[i][1] == 't':
                curPower = self.myModel.get_wifi_tail_energy(diffT)
                # check if returned is power (watt) or energy (joule)
                if self.myModel.using_power:
                    # subtract base (background) if desired
                    curPower -= power_base
                    energy = curPower * diffT
                else:
                    curPower -= power_base * diffT
                    energy = curPower
                self.wifi_tail_energy += energy
                self.wifi_tail_time += diffT
            else:
                self.logger.debug("{0}".format(self.data_tcpdump[i]))
                self.logger.error(
                    "net_state is not recognized: {0}".format(
                        self.data_tcpdump[i][1]))
            # only calculate throughput if time difference is large enough
            diffT = self.data_tcpdump[i][0] - self.wifi_instant_thrpt[-1][0]
            if diffT > 0.1:
                thrpt = 1.0 * tmp_data_size / diffT
                # reset the previous thrpt
                self.wifi_instant_thrpt[-1][1] = thrpt
                # add a new one
                self.wifi_instant_thrpt.append(
                    [self.data_tcpdump[i][0], 0])
                tmp_data_size = 0
        # the last thrpt
        if diffT > 0:
            self.wifi_instant_thrpt[-1][1] = 1.0 * tmp_data_size / diffT
            self.wifi_instant_thrpt.append(
                [self.data_tcpdump[-2][0], 0])
            self.wifi_instant_thrpt.append(
                [self.data_tcpdump[-1][0], 0])
        # self.logger.debug(self.wifi_instant_thrpt)
        # energy
        self.wifi_energy = self.wifi_active_energy + self.wifi_tail_energy
        self.wifi_time = self.wifi_active_time + self.wifi_tail_time
        try:
            self.wifi_power = self.wifi_energy / self.wifi_time
        except:
            self.wifi_power = -1
        self.logger.debug("parse_wifi_energy ended")

    def generate_result_summary(
            self,
            cpu=True, wifi=True, f_suffix="", details=True):
        '''
        Generate summary of the results
        '''
        total_energy = self.cpu_energy_total + self.wifi_energy
        total_time = self.wifi_time if wifi else self.cpu_time_total
        avg_power = total_energy / total_time

        self.logger.info(
            "total energy: {0:.4f}mJ; time: {1:.4f}s; power: {2:.4f}mW".format(
                total_energy, total_time, avg_power))
        self.logger.info(
            "avg logging freq: {0:.4f}s/record".format(self.avg_log_freq))

        # if write to file, first generate overview
        f = None
        if self.output_path is not None:
            overview_fn = "{0}/result_overview.csv".format(self.output_path)
            if os.path.isfile(overview_fn):
                f = open(overview_fn, 'ab')
            else:
                f = open(overview_fn, 'wb')
                # first line description
                f.write('#data_size(MB),avg_thrpt(Mbps),' +
                        'total_energy(mJ),total_time(s),avg_total_pwr(mW),' +
                        'avg_logging_freq(s/record)')
                if cpu:
                    f.write(',cpu_energy(mJ),' +
                            'cpu_time(s),avg_cpu_pwr(mW),' +
                            ','.join(
                                ['avg_cpu{0}_util(%)'.format(
                                    x) for x in xrange(
                                    len(self.cpu_utils_avg) - 1)]) +
                            ',avg_cpu_util(%)')
                if wifi:
                    f.write(',wifi_energy(mJ),wifi_time(s),avg_wifi_pwr(mW),' +
                            'wifi_active_energy(mJ),wifi_idle_energy(mJ)')
                if details:
                    f.write(',cpu_user_util(%),cpu_nice_util(%)' +
                            ',cpu_system_util(%),cpu_iowait_util(%)' +
                            ',cpu_irq_util(%),cpu_softirq_util(%)')
                f.write('\n')
            f.write('{0:.2f},'.format(self.data_size / 1000000.0))
            f.write('{0:.2f},'.format(self.wifi_avg_thrpt / 1000000.0 * 8))
            f.write('{0:.8f},{1:.8f},{2:.8f},'.format(
                total_energy, total_time, avg_power))
            f.write('{0:.8f}'.format(self.avg_log_freq))

        # if output cpu
        if cpu:
            self.logger.info(
                "total cpu energy: {0:.4f}mJ".format(self.cpu_energy_total))
            self.logger.info(
                "total cpu time: {0:.4f}s".format(self.cpu_time_total))
            self.logger.info(
                "average cpu power: {0:.4f}mW".format(self.cpu_power_avg))
            tmp = ",".join(["{0:.2f}".format(x * 100)
                            for x in self.cpu_utils_avg])
            self.logger.info(
                "average cpu util (%): {0}".format(tmp))
            # write to file
            if f is not None:
                f.write(',{0:.8f},{1:.8f},{2:.8f},{3}'.format(
                    self.cpu_energy_total, self.cpu_time_total,
                    self.cpu_power_avg, tmp))

        # if output wifi
        if wifi:
            self.logger.info(
                "total wifi energy: {0:.4f}mJ".format(self.wifi_energy))
            self.logger.info(
                "total wifi time: {0:.4f}s".format(self.wifi_time))
            self.logger.info(
                "avg wifi power: {0:.4f}mW".format(self.wifi_power))
            self.logger.info(
                "active wifi {0:.4f}mJ vs. idle {1:.4f}mJ".format(
                    self.wifi_active_energy, self.wifi_tail_energy))
            # write to file
            if f is not None:
                f.write(',{0:.8f},{1:.8f},{2:.8f},{3:.8f},{4:.8f}'.format(
                    self.wifi_energy, self.wifi_time, self.wifi_power,
                    self.wifi_active_energy, self.wifi_tail_energy))

        if details:
            self.logger.info(
                "cpu user util: {0:.2f}%".format(self.cpu_user_util * 100))
            self.logger.info(
                "cpu nice util: {0:.2f}%".format(self.cpu_nice_util * 100))
            self.logger.info(
                "cpu system util: {0:.2f}%".format(self.cpu_system_util * 100))
            self.logger.info(
                "cpu iowait util: {0:.2f}%".format(self.cpu_iowait_util * 100))
            self.logger.info(
                "cpu irq util: {0:.2f}%".format(self.cpu_irq_util * 100))
            self.logger.info(
                "cpu softirq util: {0:.2f}%".format(
                    self.cpu_softirq_util * 100))
            if f is not None:
                f.write(
                    ',{0:.4f},{1:.4f},{2:.4f},{3:.4f},{4:.4f},{5:.4f}'.format(
                        self.cpu_user_util * 100, self.cpu_nice_util * 100,
                        self.cpu_system_util * 100, self.cpu_iowait_util * 100,
                        self.cpu_irq_util * 100, self.cpu_softirq_util * 100))

        if f is not None:
            f.write('\n')
            self.logger.info(
                "Wrote to file {0}".format(overview_fn))
            f.close()

        # now generate instant wifi thrpt
        if wifi and self.output_path is not None:
            fn = "{0}/result_wifi_instant_{1:.2f}MB{2}.csv".format(
                self.output_path, self.data_size / 1000000.0, f_suffix)
            f = open(fn, 'wb')
            # description
            f.write('#time(s),time_delta(s),throughput(Mbps)\n')
            for i in xrange(len(self.wifi_instant_thrpt) - 1):
                f.write('{0:.2f},{1:.2f},{2:.2f}\n'.format(
                    self.wifi_instant_thrpt[i][0],
                    self.wifi_instant_thrpt[i][0] -
                    self.wifi_instant_thrpt[0][0],
                    self.wifi_instant_thrpt[i][1] / 1000000.0 * 8))
            # f.write('{0:.2f},{1:.2f},{2:.2f}\n'.format(
            #     self.wifi_instant_thrpt[-1][0],
            #     self.wifi_instant_thrpt[-1][0] -
            #     self.wifi_instant_thrpt[0][0],
            #     0))
            f.close()
            self.logger.info("Wrote to file {0}".format(fn))

        # now generate instant cpu
        if cpu and self.output_path is not None:
            fn = "{0}/result_cpu_instant_{1:.2f}MB{2}.csv".format(
                self.output_path, self.data_size / 1000000.0, f_suffix)
            f = open(fn, 'wb')
            # description
            num_of_cores = len(self.instant_freqs[0])
            f.write('#time(s),time_delta(s),' +
                    ','.join(['freq_cpu{0}'.format(x)
                              for x in xrange(num_of_cores)]) +
                    ',' +
                    ','.join(['util_cpu{0}'.format(x)
                              for x in xrange(num_of_cores)]) +
                    ',util_cpu,' +
                    'power\n')
            for i in xrange(len(self.instant_freqs)):
                f.write('{0:.2f},'.format(self.data_cpu[i+1][0]))
                f.write('{0:.2f},'.format(
                    self.data_cpu[i][0] - self.data_cpu[0][0]))
                for freq in self.instant_freqs[i]:
                    f.write('{0:d},'.format(freq))
                for util in self.instant_utils[i]:
                    f.write('{0:.2f},'.format(util * 100))
                f.write('{0:.8f}\n'.format(self.instant_power[i]))
            f.close()
            self.logger.info("Wrote to file {0}".format(fn))

if __name__ == "__main__":
    # cpuFile = sys.argv[1]
    # cpuFile = "./models/test/1485560673559.cpu"
    # tcpdumpFile = "./models/test/tcpdump_wifionly_1485560673559"
    # ssFile = "./models/test/1485560673559.ss"
    cpuFile = "./models/test2/1487031992798.cpu"
    tcpdumpFile = "./models/test2/tcpdump_wifionly_1487031992798"
    ssFile = "./models/test2/1487031992798.ss"
    myObj = EnergyAnalyzer(
        "shamu", isDebugging=False, unit="mW",
        output_path="./models/test2/")
    myObj.read_wifi_log(
        tcpdumpFile,
        size_limit=None,  # 1000*1000*90,
        fp_sslogger=ssFile, tcpdump_filter="host 192.168.10.1")
    myObj.parse_wifi_energy()
    myObj.read_cpu_log(
        cpuFile,
        startT=myObj.net_start_time, endT=myObj.net_end_time)
    myObj.parse_cpu_energy()
    myObj.generate_result_summary(f_suffix="_test")
