# Copyright (c) 2003-2005 Jimmy Retzlaff, 2008 Konstantin Yegupov
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

# Low level interface - see UnRARDLL\UNRARDLL.TXT

from __future__ import generators

import ctypes
import ctypes.wintypes
import os
import os.path
import re
import time
import sys

from .rar_exceptions import *

if sys.version_info > (3,3):
    import faulthandler
    faulthandler.enable()

if sys.version_info[0] >= 3:
    def string_from_bytes(s):
        return s.decode(sys.getdefaultencoding())
    def bytes_from_string(s):
        return s.encode(sys.getdefaultencoding())
else:
    def string_from_bytes(s):
        return s
    def bytes_from_string(s):
        return s

ERAR_END_ARCHIVE = 10
ERAR_NO_MEMORY = 11
ERAR_BAD_DATA = 12
ERAR_BAD_ARCHIVE = 13
ERAR_UNKNOWN_FORMAT = 14
ERAR_EOPEN = 15
ERAR_ECREATE = 16
ERAR_ECLOSE = 17
ERAR_EREAD = 18
ERAR_EWRITE = 19
ERAR_SMALL_BUF = 20
ERAR_UNKNOWN = 21
ERAR_MISSING_PASSWORD = 22

RAR_OM_LIST = 0
RAR_OM_EXTRACT = 1

RAR_SKIP = 0
RAR_TEST = 1
RAR_EXTRACT = 2

RAR_VOL_ASK = 0
RAR_VOL_NOTIFY = 1

RAR_DLL_VERSION = 3

# enum UNRARCALLBACK_MESSAGES
UCM_CHANGEVOLUME = 0
UCM_PROCESSDATA = 1
UCM_NEEDPASSWORD = 2

architecture_bits = ctypes.sizeof(ctypes.c_voidp) * 8
dll_name = "unrar.dll"
if architecture_bits == 64:
    dll_name = "x64\\unrar64.dll"

volume_naming1 = re.compile("[.]r([0-9]{2})$")
volume_naming2 = re.compile("[.]([0-9]{3})[.]rar$")
volume_naming3 = re.compile("[.]part([0-9]+)[.]rar$")

try:
    unrar = ctypes.WinDLL(
        os.path.join(os.path.split(__file__)[0], 'UnRARDLL', dll_name))
except WindowsError:
    unrar = ctypes.WinDLL(dll_name)

class RAROpenArchiveDataEx(ctypes.Structure):
    def __init__(self, ArcName=None, ArcNameW=u'', OpenMode=RAR_OM_LIST):
        self.CmtBuf = ctypes.c_buffer(64 * 1024)
        ctypes.Structure.__init__(self, ArcName=ArcName, ArcNameW=ArcNameW,
                                  OpenMode=OpenMode,
                                  _CmtBuf=ctypes.addressof(self.CmtBuf),
                                  CmtBufSize=ctypes.sizeof(self.CmtBuf))

    _fields_ = [
        ('ArcName', ctypes.c_char_p),
        ('ArcNameW', ctypes.c_wchar_p),
        ('OpenMode', ctypes.c_uint),
        ('OpenResult', ctypes.c_uint),
        ('_CmtBuf', ctypes.c_voidp),
        ('CmtBufSize', ctypes.c_uint),
        ('CmtSize', ctypes.c_uint),
        ('CmtState', ctypes.c_uint),
        ('Flags', ctypes.c_uint),
        ('Reserved', ctypes.c_uint * 32),
    ]


class RARHeaderDataEx(ctypes.Structure):
    def __init__(self):
        self.CmtBuf = ctypes.c_buffer(64 * 1024)
        ctypes.Structure.__init__(self, _CmtBuf=ctypes.addressof(self.CmtBuf),
                                  CmtBufSize=ctypes.sizeof(self.CmtBuf))

    _fields_ = [
        ('ArcName', ctypes.c_char * 1024),
        ('ArcNameW', ctypes.c_wchar * 1024),
        ('FileName', ctypes.c_char * 1024),
        ('FileNameW', ctypes.c_wchar * 1024),
        ('Flags', ctypes.c_uint),
        ('PackSize', ctypes.c_uint),
        ('PackSizeHigh', ctypes.c_uint),
        ('UnpSize', ctypes.c_uint),
        ('UnpSizeHigh', ctypes.c_uint),
        ('HostOS', ctypes.c_uint),
        ('FileCRC', ctypes.c_uint),
        ('FileTime', ctypes.c_uint),
        ('UnpVer', ctypes.c_uint),
        ('Method', ctypes.c_uint),
        ('FileAttr', ctypes.c_uint),
        ('_CmtBuf', ctypes.c_voidp),
        ('CmtBufSize', ctypes.c_uint),
        ('CmtSize', ctypes.c_uint),
        ('CmtState', ctypes.c_uint),
        ('Reserved', ctypes.c_uint * 1024),
    ]


def DosDateTimeToTimeTuple(dosDateTime):
    """Convert an MS-DOS format date time to a Python time tuple.
    """
    dos_date = dosDateTime >> 16
    dos_time = dosDateTime & 0xffff
    day = dos_date & 0x1f
    month = (dos_date >> 5) & 0xf
    year = 1980 + (dos_date >> 9)
    second = 2 * (dos_time & 0x1f)
    minute = (dos_time >> 5) & 0x3f
    hour = dos_time >> 11
    return time.localtime(