# -*- coding: utf-8 -*-
"""Wrap process I/O pipe communication using pywin32."""

# yapf: disable

# Standard library imports
from ctypes import windll
from ctypes.wintypes import BOOL, DWORD, HANDLE, LPCVOID, LPVOID
import ctypes
import sys

# Local imports
from .cywinpty import Agent


PY2 = sys.version_info[0] == 2

# yapf: enable

OPEN_EXISTING = 3
GENERIC_WRITE = 0x40000000
GENERIC_READ = 0x80000000

LARGE_INTEGER = ctypes.c_ulong
PLARGE_INTEGER = ctypes.POINTER(LARGE_INTEGER)
LPOVERLAPPED = LPVOID

# LPDWORD is not in ctypes.wintypes on Python 2
LPDWORD = ctypes.POINTER(DWORD)

ReadFile = windll.kernel32.ReadFile
ReadFile.restype = BOOL
ReadFile.argtypes = [HANDLE, LPVOID, DWORD, LPDWORD, LPOVERLAPPED]

WriteFile = windll.kernel32.WriteFile
WriteFile.restype = BOOL
WriteFile.argtypes = [HANDLE, LPCVOID, DWORD, LPDWORD, LPOVERLAPPED]


class PTY(Agent):
    """
    This class provides a pywin32 communication wrapper around winpty process
    communication pipes.

    Inherits all Cython winpty agent functionality and properties.
    """

    def __init__(self, cols, rows):
        """Initialize a new Pseudo Terminal of size ``(cols, rows)``."""
        Agent.__init__(self, cols, rows, True)
        self.conin_pipe = windll.kernel32.CreateFileW(
            self.conin_pipe_name, GENERIC_WRITE, 0, None, OPEN_EXISTING, 0,
            None
        )
        self.conout_pipe = windll.kernel32.CreateFileW(
            self.conout_pipe_name, GENERIC_READ, 0, None, OPEN_EXISTING, 0,
            None
        )

    def read(self, length=1000):
        """
        Read ``length`` bytes from current process output stream.

        Note: This method is not fully non-blocking, however it
        behaves like one.
        """
        size_p = PLARGE_INTEGER(LARGE_INTEGER(0))
        windll.kernel32.GetFileSizeEx(self.conout_pipe, size_p)
        size = size_p[0]
        length = min(size, length)
        data = ctypes.create_string_buffer(length)
        if length > 0:
            ReadFile(self.conout_pipe, data, length, None, None)
        return data.value

    def write(self, data):
        """Write string data to current process input stream."""
        data = data.encode('utf-8')
        data_p = ctypes.create_string_buffer(data)
        num_bytes = PLARGE_INTEGER(LARGE_INTEGER(0))
        bytes_to_write = len(data)
        success = WriteFile(
            self.conin_pipe, data_p, bytes_to_write, num_bytes, None
        )
        return success, num_bytes[0]

    def close(self):
        """Close all communication process streams."""
        windll.kernel32.CloseHandle(self.conout_pipe)
        windll.kernel32.CloseHandle(self.conin_pipe)

    def iseof(self):
        """Check if current process streams are still open."""
        succ = windll.kernel32.PeekNamedPipe(
            self.conout_pipe, None, None, None, None, None
        )
        return not bool(succ)
