# Volatility
#
# Authors:
# Mike Auty <mike.auty@gmail.com>
#
# This file is part of Volatility.
#
# Volatility is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# Volatility is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Volatility.  If not, see <http://www.gnu.org/licenses/>.
#

import os, time, calendar
import datetime
import volatility.conf as conf
import volatility.debug as debug
try:
    import pytz
    tz_pytz = True
except ImportError:
    tz_pytz = False
config = conf.ConfObject()

class OffsetTzInfo(datetime.tzinfo):
    """Timezone implementation that allows offsets specified in seconds"""

    def __init__(self, offset = None, *args, **kwargs):
        """Accepts offset in seconds"""
        self.offset = offset
        datetime.tzinfo.__init__(self, *args, **kwargs)

    def set_offset(self, offset):
        """Simple setter for offset"""
        self.offset = offset

    def utcoffset(self, dt):
        """Returns the offset from UTC"""
        if self.offset is None:
            return None
        return datetime.timedelta(seconds = self.offset) + self.dst(dt)

    def dst(self, _dt):
        """We almost certainly can't know about DST, so we say it's always off"""
        # FIXME: Maybe we can know or make guesses about DST? 
        return datetime.timedelta(0)

    def tzname(self, _dt):
        """Return a useful timezone name"""
        if self.offset is None:
            return "UNKNOWN"

        return ""

class UTC(datetime.tzinfo):
    """Concrete instance of the UTC timezone"""

    def utcoffset(self, _dt):
        """Returns an offset from UTC of 0"""
        return datetime.timedelta(0)

    def dst(self, _dt):
        """Returns no daylight savings offset"""
        return datetime.timedelta(0)

    def tzname(self, _dt):
        """Returns the timezone name"""
        return "UTC"

def display_datetime(dt, custom_tz = None):
    """Returns a string from a datetime according to the display TZ (or a custom one"""
    timeformat = "%Y-%m-%d %H:%M:%S %Z%z"
    if dt.tzinfo is not None and dt.tzinfo.utcoffset(dt) is not None:
        if custom_tz is not None:
            dt = dt.astimezone(custom_tz)
        elif config.TZ is not None:
            if isinstance(config.TZ, str):
                secs = calendar.timegm(dt.timetuple())
                os.environ['TZ'] = config.TZ
                time.tzset()
                # Remove the %z which appears not to work
                timeformat = timeformat[:-2]
                return time.strftime(timeformat, time.localtime(secs))
            else:
                dt = dt.astimezone(config.tz)
    return ("{0:" + timeformat + "}").format(dt)

def tz_from_string(_option, _opt_str, value, parser):
    """Stores a tzinfo object from a string"""
    if value is not None:
        if value[0] in ['+', '-']:
            # Handed a numeric offset, create an OffsetTzInfo
            valarray = [value[i:i + 2] for i in range(1, len(value), 2)]
            multipliers = [3600, 60]
            offset = 0
            for i in range(min(len(valarray), len(multipliers))):
                offset += int(valarray[i]) * multipliers[i]
            if value[0] == '-':
                offset = -offset
            timezone = OffsetTzInfo(offset = offset)
        else:
            # Value is a lookup, choose pytz over time.tzset
            if tz_pytz:
                try:
                    timezone = pytz.timezone(value)
                except pytz.UnknownTimeZoneError:
                    debug.error("Unknown display timezone specified")
            else:
                if not hasattr(time, 'tzset'):
                    debug.error("This operating system doesn't support tzset, please either specify an offset (eg. +1000) or install pytz")
                timezone = value
        parser.values.tz = timezone

config.add_option("TZ", action = "callback", callback = tz_from_string,
                  cache_invalidator = False,
                  help = "Sets the (Olson) timezone for displaying timestamps using pytz (if installed) or tzset",
                  default = None, nargs = 1, type = str)