from datetime import datetime, timedelta
from pychrysalide.arch import vmpa
import string
import struct


def data_to_number(content, addr, order, fmt):
    """Convert to a number, if possible."""

    size =  struct.calcsize(order + fmt)

    data = content.read_raw(addr, size)

    value = struct.unpack(order + fmt, data)[0]

    return str(value)


def data_to_time(content, addr, order, fmt):
    """Convert to a number, if possible."""

    size =  struct.calcsize(order + fmt)

    data = content.read_raw(addr, size)

    value = struct.unpack(order + fmt, data)[0]

    return str(datetime(1970, 1, 1) + timedelta(seconds=value))


# Cf. FILETIME structure
# https://docs.microsoft.com/fr-fr/windows/win32/api/minwinbase/ns-minwinbase-filetime

def data_to_filetime(content, addr, order):
    """Convert to a Windows FILETIME, if possible."""

    data = content.read_raw(addr, 8)

    value = struct.unpack(order + 'Q', data)[0]

    us = value / 10.

    return str(datetime(1601, 1, 1) + timedelta(microseconds=us))


# Cf. DosDateTimeToFileTime()
# https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-dosdatetimetofiletime

def data_to_dos_time(content, addr, order):
    """Convert to a MS-DOS time, if possible."""

    data = content.read_raw(addr, 2)

    value = struct.unpack(order + 'H', data)[0]

    seconds = (value & 0x1f) * 2
    minutes = (value & 0x7e0) >> 5
    hours = (value & 0xf800) >> 11

    return '%02u:%02u:%02u' % (hours, minutes, seconds)

def data_to_dos_date(content, addr, order):
    """Convert to a MS-DOS date, if possible."""

    data = content.read_raw(addr, 2)

    value = struct.unpack(order + 'H', data)[0]

    day = (value & 0x1f)
    month = (value & 0x1e0) >> 5
    year = ((value & 0xfe00) >> 9) + 1980

    return '%u/%u/%u' % (month, day, year)


def data_to_char(content, addr, order):
    """Convert to a character, if possible."""

    data = content.read_raw(addr, 1)

    value = struct.unpack(order + 'c', data)[0]

    ch = chr(value[0])

    return ch if ch in string.printable else '-'


def data_to_ansi(content, addr, order):
    """Convert to an ANSI string, if possible."""

    result = None

    while True:

        try:

            data = content.read_raw(addr, 1)

            value = struct.unpack(order + 'c', data)[0]

            ch = chr(value[0])

            if not(ch in string.printable):
                break

            if result:
                result += ch
            else:
                result = ch

        except:
            pass

    return result if result else '-'


def _data_to_utf(content, addr, utf):
    """Convert to an UTF-X string, if possible."""

    result = None

    length = 0

    while True:

        try:

            start = vmpa(addr.phys, 0)
            data = content.read_raw(start, length + 1)

            result = data.decode(utf)

            length += 1

        except Exception as e:
            break

    if length > 0:

        data = content.read_raw(addr, length)

        result = data.decode('utf-8')

    else:
        result = '-'

    return result


def data_to_utf8(content, addr, order):
    """Convert to an UTF-8 string, if possible."""

    return _data_to_utf(content, addr, 'utf-8')


def data_to_utf16(content, addr, order):
    """Convert to an UTF-16 string, if possible."""

    return _data_to_utf(content, addr, 'utf-16')