from datetime import datetime, timedelta
from struct import unpack, calcsize

from pytzpure.tzinfo import StaticTzInfo, DstTzInfo, memorized_ttinfo
from pytzpure.tzinfo import memorized_datetime, memorized_timedelta

from pytzpure.tz_descriptor import TzDescriptor
from pytzpure.loader import load_module


def _byte_string(s):
    """Cast a string or byte string to an ASCII byte string."""
    return s.encode('US-ASCII')

_NULL = _byte_string('\0')

def _std_string(s):
    """Cast a string or byte string to an ASCII string."""
    return str(s.decode('US-ASCII'))

def build_tzinfo(zone):#, fp):
#    head_fmt = '>4s c 15x 6l'
#    head_size = calcsize(head_fmt)
#    (magic, format, ttisgmtcnt, ttisstdcnt,leapcnt, timecnt,
#        typecnt, charcnt) =  unpack(head_fmt, fp.read(head_size))
#
#    # Make sure it is a tzfile(5) file
#    assert magic == _byte_string('TZif'), 'Got magic %s' % repr(magic)
#
#    # Read out the transition times, localtime indices and ttinfo structures.
#    data_fmt = '>%(timecnt)dl %(timecnt)dB %(ttinfo)s %(charcnt)ds' % dict(
#        timecnt=timecnt, ttinfo='lBB'*typecnt, charcnt=charcnt)
#    data_size = calcsize(data_fmt)
#    data = unpack(data_fmt, fp.read(data_size))
#
#    # make sure we unpacked the right number of values
#    assert len(data) == 2 * timecnt + 3 * typecnt + 1
#    transitions = [memorized_datetime(trans)
#                   for trans in data[:timecnt]]
#    lindexes = list(data[timecnt:2 * timecnt])
#    ttinfo_raw = data[2 * timecnt:-1]
#    tznames_raw = data[-1]
#    del data
#
#    # Process ttinfo into separate structs
#    ttinfo = []
#    tznames = {}
#    i = 0
#    while i < len(ttinfo_raw):
#        # have we looked up this timezone name yet?
#        tzname_offset = ttinfo_raw[i+2]
#        if tzname_offset not in tznames:
#            nul = tznames_raw.find(_NULL, tzname_offset)
#            if nul < 0:
#                nul = len(tznames_raw)
#            tznames[tzname_offset] = _std_string(
#                tznames_raw[tzname_offset:nul])
#        ttinfo.append((ttinfo_raw[i],
#                       bool(ttinfo_raw[i+1]),
#                       tznames[tzname_offset]))
#        i += 3
#
#    # Now build the timezone object
#    if len(transitions) == 0:
#        ttinfo[0][0], ttinfo[0][2]
#        cls = type(zone, (StaticTzInfo,), dict(
#            zone=zone,
#            _utcoffset=memorized_timedelta(ttinfo[0][0]),
#            _tzname=ttinfo[0][2]))
#    else:
#        # Early dates use the first standard time ttinfo
#        i = 0
#        while ttinfo[i][1]:
#            i += 1
#        if ttinfo[i] == ttinfo[lindexes[0]]:
#            transitions[0] = datetime.min
#        else:
#            transitions.insert(0, datetime.min)
#            lindexes.insert(0, i)
#
#        # calculate transition info
#        transition_info = []
#        for i in range(len(transitions)):
#            inf = ttinfo[lindexes[i]]
#            utcoffset = inf[0]
#            if not inf[1]:
#                dst = 0
#            else:
#                for j in range(i-1, -1, -1):
#                    prev_inf = ttinfo[lindexes[j]]
#                    if not prev_inf[1]:
#                        break
#                dst = inf[0] - prev_inf[0] # dst offset
#
#                # Bad dst? Look further. DST > 24 hours happens when
#                # a timzone has moved across the international dateline.
#                if dst <= 0 or dst > 3600*3:
#                    for j in range(i+1, len(transitions)):
#                        stdinf = ttinfo[lindexes[j]]
#                        if not stdinf[1]:
#                            dst = inf[0] - stdinf[0]
#                            if dst > 0:
#                                break # Found a useful std time.
#
#            tzname = inf[2]
#
#            # Round utcoffset and dst to the nearest minute or the
#            # datetime library will complain. Conversions to these timezones
#            # might be up to plus or minus 30 seconds out, but it is
#            # the best we can do.
#            utcoffset = int((utcoffset + 30) // 60) * 60
#            dst = int((dst + 30) // 60) * 60
#            transition_info.append(memorized_ttinfo(utcoffset, dst, tzname))

# transitions:
#[datetime.datetime(1, 1, 1, 0, 0),
# datetime.datetime(1918, 3, 31, 7, 0),
# datetime.datetime(1938, 9, 25, 6, 0),
# datetime.datetime(2037, 11, 1, 6, 0)]

# transition_info:
#[(datetime.timedelta(-1, 68400), datetime.timedelta(0), 'EST'),
# (datetime.timedelta(-1, 72000), datetime.timedelta(0, 3600), 'EDT'),
# (datetime.timedelta(-1, 68400), datetime.timedelta(0), 'EST'),
# (datetime.timedelta(-1, 72000), datetime.timedelta(0, 3600), 'EDT'),
# (datetime.timedelta(-1, 68400), datetime.timedelta(0), 'EST'),
# (datetime.timedelta(-1, 68400), datetime.timedelta(0), 'EST')]
#
#        cls = type(zone, (DstTzInfo,), dict(
#            zone=zone,
#            _utc_transition_times=transitions,
#            _transition_info=transition_info))
#
#    return cls()

    # Restore a pytz-object from Python-stored data. We load the values that
    # either a DstTzInfo- (above) -or- StaticTzInfo-type (farther above) 
    # parent-class would require.
    tzd = TzDescriptor.load_from_file(zone)

    if tzd.parent_class_name == 'StaticTzInfo':
        cls = type(tzd.zone_name, (StaticTzInfo,), dict(
                zone=tzd.zone_name,
                _utcoffset=tzd.utcoffset_formal,
                _tzname=tzd.tzname))
    else:
        cls = type(tzd.zone_name, (DstTzInfo,), dict(
                zone=tzd.zone_name,
                _utc_transition_times=tzd.utc_transition_times_list_formal,
                _transition_info=tzd.transition_info_list_formal))

    return cls()

