﻿using UnityEngine;
using System;
using System.Text;
using System.Runtime.InteropServices;

#pragma warning disable 114, 465

namespace uDesktopDuplication
{

public enum Message
{
    None = -1,
    Reinitialized = 0,
    TextureSizeChanged = 1,
}

public enum CursorShapeType
{
    Unspecified = 0,
    MonoChrome = 1,
    Color = 2,
    MaskedColor = 4,
}

public enum MonitorRotation
{
    Unspecified = 0,
    Identity = 1,
    Rotate90 = 2,
    Rotate180 = 3,
    Rotate270 = 4
}

public enum DuplicatorState
{
    NotSet = -1,
    Ready = 0,
    Running = 1,
    InvalidArg = 2,
    AccessDenied = 3,
    Unsupported = 4,
    CurrentlyNotAvailable = 5,
    SessionDisconnected = 6,
    AccessLost = 7,
    TextureSizeInconsistent = 8,
    Unknown = 999,
}

public enum DebugMode
{
    None = 0,
    File = 1,
    UnityLog = 2, /* currently has bug when app exits. */
}

[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    [MarshalAs(UnmanagedType.I4)]
    public int left;
    [MarshalAs(UnmanagedType.I4)]
    public int top;
    [MarshalAs(UnmanagedType.I4)]
    public int right;
    [MarshalAs(UnmanagedType.I4)]
    public int bottom;
}

[StructLayout(LayoutKind.Sequential)]
public struct DXGI_OUTDUPL_MOVE_RECT
{
    public RECT source;
    public RECT destination;
}

public static class Lib
{
    const string dllName = "uDesktopDuplication";

    public delegate void MessageHandler(Message message);
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void DebugLogDelegate(string str);

    [DllImport(dllName)]
    public static extern bool IsInitialized();
    [DllImport(dllName)]
    public static extern void Initialize();
    [DllImport(dllName)]
    public static extern void Finalize();
    [DllImport(dllName)]
    public static extern void Reinitialize();
    [DllImport(dllName)]
    public static extern void Update();
    [DllImport(dllName)]
    public static extern Message PopMessage();
    [DllImport(dllName)]
    public static extern void EnableDebug();
    [DllImport(dllName)]
    public static extern void DisableDebug();
    [DllImport(dllName)]
    public static extern void SetDebugMode(DebugMode mode);
    [DllImport(dllName)]
    public static extern void SetLogFunc(DebugLogDelegate func);
    [DllImport(dllName)]
    public static extern void SetErrorFunc(DebugLogDelegate func);
    [DllImport(dllName)]
    public static extern int GetMonitorCount();
    [DllImport(dllName)]
    public static extern bool HasMonitorCountChanged();
    [DllImport(dllName)]
    public static extern int GetCursorMonitorId();
    [DllImport(dllName)]
    public static extern int GetTotalWidth();
    [DllImport(dllName)]
    public static extern int GetTotalHeight();
    [DllImport(dllName)]
    public static extern IntPtr GetRenderEventFunc();
    [DllImport(dllName)]
    public static extern int GetId(int id);
    [DllImport(dllName)]
    public static extern DuplicatorState GetState(int id);
    [DllImport(dllName)]
    public static extern void GetName(int id, StringBuilder buf, int len);
    [DllImport(dllName)]
    public static extern int GetLeft(int id);
    [DllImport(dllName)]
    public static extern int GetRight(int id);
    [DllImport(dllName)]
    public static extern int GetTop(int id);
    [DllImport(dllName)]
    public static extern int GetBottom(int id);
    [DllImport(dllName)]
    public static extern int GetWidth(int id);
    [DllImport(dllName)]
    public static extern int GetHeight(int id);
    [DllImport(dllName)]
    public static extern int GetDpiX(int id);
    [DllImport(dllName)]
    public static extern int GetDpiY(int id);
    [DllImport(dllName)]
    public static extern bool IsHDR(int id);
    [DllImport(dllName)]
    public static extern MonitorRotation GetRotation(int id);
    [DllImport(dllName)]
    public static extern bool IsPrimary(int id);
    [DllImport(dllName)]
    public static extern bool IsCursorVisible();
    [DllImport(dllName)]
    public static extern int GetCursorX();
    [DllImport(dllName)]
    public static extern int GetCursorY();
    [DllImport(dllName)]
    public static extern int GetCursorShapeWidth();
    [DllImport(dllName)]
    public static extern int GetCursorShapeHeight();
    [DllImport(dllName)]
    public static extern int GetCursorShapePitch();
    [DllImport(dllName)]
    public static extern CursorShapeType GetCursorShapeType();
    [DllImport(dllName)]
    public static extern void GetCursorTexture(IntPtr ptr);
    [DllImport(dllName)]
    public static extern int GetCursorHotSpotX();
    [DllImport(dllName)]
    public static extern int GetCursorHotSpotY();
    [DllImport(dllName)]
    public static extern int SetTexturePtr(int id, IntPtr ptr);
    [DllImport(dllName)]
    public static extern IntPtr GetSharedTextureHandle(int id);
    [DllImport(dllName)]
    public static extern int GetMoveRectCount(int id);
    [DllImport(dllName, EntryPoint = "GetMoveRects")]
    private static extern IntPtr GetMoveRects_Internal(int id);
    [DllImport(dllName)]
    public static extern int GetDirtyRectCount(int id);
    [DllImport(dllName, EntryPoint = "GetDirtyRects")]
    private static extern IntPtr GetDirtyRects_Internal(int id);
    [DllImport(dllName, EntryPoint = "GetPixels")]
    private static extern bool GetPixels_Internal(int id, IntPtr ptr, int x, int y, int width, int height);
    [DllImport(dllName)]
    public static extern IntPtr GetBuffer(int id);
    [DllImport(dllName)]
    public static extern bool HasBeenUpdated(int id);
    [DllImport(dllName)]
    public static extern bool UseGetPixels(int id, bool use);
    [DllImport(dllName)]
    public static extern void SetFrameRate(uint frameRate);

    public static string GetName(int id)
    {
        var buf = new StringBuilder(32);
        GetName(id, buf, buf.Capacity);
        return buf.ToString();
    }

    public static DXGI_OUTDUPL_MOVE_RECT[] GetMoveRects(int id)
    {
        var count = GetMoveRectCount(id);
        var rects = new DXGI_OUTDUPL_MOVE_RECT[count];
        var ptr = GetMoveRects_Internal(id);
        var size = Marshal.SizeOf(typeof(DXGI_OUTDUPL_MOVE_RECT));
        for (int i = 0; i < count; ++i) {
            var data = new IntPtr(ptr.ToInt64() + size * i);
            rects[i] = (DXGI_OUTDUPL_MOVE_RECT)Marshal.PtrToStructure(data, typeof(DXGI_OUTDUPL_MOVE_RECT));
        }
        return rects;
    }

    public static RECT[] GetDirtyRects(int id)
    {
        var count = GetDirtyRectCount(id);
        var rects = new RECT[count];
        var ptr = GetDirtyRects_Internal(id);
        var size = Marshal.SizeOf(typeof(RECT));
        for (int i = 0; i < count; ++i) {
            var data = new IntPtr(ptr.ToInt64() + size * i);
            rects[i] = (RECT)Marshal.PtrToStructure(data, typeof(RECT));
        }
        return rects;
    }

    public static Color32[] GetPixels(int id, int x, int y, int width, int height)
    {
        var color = new Color32[width * height];       
        GetPixels(id, color, x, y, width, height);
        return color;
    }

    public static bool GetPixels(int id, Color32[] colors, int x, int y, int width, int height)
    {
        if (colors.Length < width * height) {
            Debug.LogErrorFormat("colors is small.", id, x, y, width, height);
            return false;
        }
		var handle = GCHandle.Alloc(colors, GCHandleType.Pinned);
		var ptr = handle.AddrOfPinnedObject();
        if (!GetPixels_Internal(id, ptr, x, y, width, height)) {
            Debug.LogErrorFormat("GetPixels({0}, {1}, {2}, {3}, {4}) failed.", id, x, y, width, height);
            return false;
        }
        handle.Free();
        return true;
    }

    public static Color32 GetPixel(int id, int x, int y)
    {
        return GetPixels(id, x, y, 1, 1)[0];
    }
}

}