#!/usr/bin/env python
# -*- coding: utf-8 -*-
# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab

from __future__ import unicode_literals, division, absolute_import, print_function

from .compatibility_utils import PY2, hexlify, bstr, bord, bchar
from loguru import logger

import datetime

if PY2:
    range = xrange

# note:  struct pack, unpack, unpack_from all require bytestring format
# data all the way up to at least python 2.7.5, python 3 okay with bytestring
import struct

from .unipath import pathof

DUMP = False
""" Set to True to dump all possible information. """


class unpackException(Exception):
    pass


def describe(data):
    txtans = ""
    hexans = hexlify(data)
    for i in data:
        if bord(i) < 32 or bord(i) > 127:
            txtans += "?"
        else:
            txtans += bchar(i).decode("latin-1")
    return '"' + txtans + '"' + " 0x" + hexans


def datetimefrompalmtime(palmtime):
    if palmtime > 0x7FFFFFFF:
        pythondatetime = datetime.datetime(
            year=1904, month=1, day=1
        ) + datetime.timedelta(seconds=palmtime)
    else:
        pythondatetime = datetime.datetime(
            year=1970, month=1, day=1
        ) + datetime.timedelta(seconds=palmtime)
    return pythondatetime


class Sectionizer:
    def __init__(self, filename):
        self.data = b""
        with open(pathof(filename), "rb") as f:
            self.data = f.read()
        self.palmheader = self.data[:78]
        self.palmname = self.data[:32]
        self.ident = self.palmheader[0x3C : 0x3C + 8]
        (self.num_sections,) = struct.unpack_from(b">H", self.palmheader, 76)
        self.filelength = len(self.data)
        sectionsdata = struct.unpack_from(
            bstr(">%dL" % (self.num_sections * 2)), self.data, 78
        ) + (self.filelength, 0)
        self.sectionoffsets = sectionsdata[::2]
        self.sectionattributes = sectionsdata[1::2]
        self.sectiondescriptions = ["" for x in range(self.num_sections + 1)]
        self.sectiondescriptions[-1] = "File Length Only"
        return

    def dumpsectionsinfo(self):
        logger.debug("Section     Offset  Length      UID Attribs Description")
        for i in range(self.num_sections):
            logger.debug(
                "%3d %3X  0x%07X 0x%05X % 8d % 7d %s"
                % (
                    i,
                    i,
                    self.sectionoffsets[i],
                    self.sectionoffsets[i + 1] - self.sectionoffsets[i],
                    self.sectionattributes[i] & 0xFFFFFF,
                    (self.sectionattributes[i] >> 24) & 0xFF,
                    self.sectiondescriptions[i],
                )
            )
        logger.debug(
            "%3d %3X  0x%07X                          %s"
            % (
                self.num_sections,
                self.num_sections,
                self.sectionoffsets[self.num_sections],
                self.sectiondescriptions[self.num_sections],
            )
        )

    def setsectiondescription(self, section, description):
        if section < len(self.sectiondescriptions):
            self.sectiondescriptions[section] = description
        else:
            logger.debug(
                "Section out of range: %d, description %s" % (section, description)
            )

    def dumppalmheader(self):
        logger.debug("Palm Database Header")
        logger.debug("Database name: " + repr(self.palmheader[:32]))
        (dbattributes,) = struct.unpack_from(b">H", self.palmheader, 32)
        logger.debug("Bitfield attributes: 0x%0X" % dbattributes,)
        if dbattributes != 0:
            print(" (",)
            if dbattributes & 2:
                print("Read-only; ",)
            if dbattributes & 4:
                print("Dirty AppInfoArea; ",)
            if dbattributes & 8:
                print("Needs to be backed up; ",)
            if dbattributes & 16:
                print("OK to install over newer; ",)
            if dbattributes & 32:
                print("Reset after installation; ",)
            if dbattributes & 64:
                print("No copying by PalmPilot beaming; ",)
            print(")")
        else:
            print("")
        logger.debug(
            "File version: %d" % struct.unpack_from(b">H", self.palmheader, 34)[0]
        )
        (dbcreation,) = struct.unpack_from(b">L", self.palmheader, 36)
        logger.debug(
            "Creation Date: "
            + str(datetimefrompalmtime(dbcreation))
            + (" (0x%0X)" % dbcreation)
        )
        (dbmodification,) = struct.unpack_from(b">L", self.palmheader, 40)
        logger.debug(
            "Modification Date: "
            + str(datetimefrompalmtime(dbmodification))
            + (" (0x%0X)" % dbmodification)
        )
        (dbbackup,) = struct.unpack_from(b">L", self.palmheader, 44)
        if dbbackup != 0:
            logger.debug(
                "Backup Date: "
                + str(datetimefrompalmtime(dbbackup))
                + (" (0x%0X)" % dbbackup)
            )
        logger.debug(
            "Modification No.: %d" % struct.unpack_from(b">L", self.palmheader, 48)[0]
        )
        logger.debug(
            "App Info offset: 0x%0X" % struct.unpack_from(b">L", self.palmheader, 52)[0]
        )
        logger.debug(
            "Sort Info offset: 0x%0X"
            % struct.unpack_from(b">L", self.palmheader, 56)[0]
        )
        logger.debug(
            "Type/Creator: %s/%s"
            % (repr(self.palmheader[60:64]), repr(self.palmheader[64:68]))
        )
        logger.debug(
            "Unique seed: 0x%0X" % struct.unpack_from(b">L", self.palmheader, 68)[0]
        )
        (expectedzero,) = struct.unpack_from(b">L", self.palmheader, 72)
        if expectedzero != 0:
            logger.debug(
                "Should be zero but isn't: %d"
                % struct.unpack_from(b">L", self.palmheader, 72)[0]
            )
        logger.debug(
            "Number of sections: %d" % struct.unpack_from(b">H", self.palmheader, 76)[0]
        )
        return

    def loadSection(self, section):
        before, after = self.sectionoffsets[section : section + 2]
        return self.data[before:after]
