/*
* BearLibTerminal C# wrapper
* Copyright (C) 2013-2014 Cfyz
*
* 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.
*
* Release date: 2014-08-13
*/

using System;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace BearLib
{
    public static class Terminal
    {        
        public const int
        
        // Keyboard scancodes 
        TK_A                = 0x04,
        TK_B                = 0x05,
        TK_C                = 0x06,
        TK_D                = 0x07,
        TK_E                = 0x08,
        TK_F                = 0x09,
        TK_G                = 0x0A,
        TK_H                = 0x0B,
        TK_I                = 0x0C,
        TK_J                = 0x0D,
        TK_K                = 0x0E,
        TK_L                = 0x0F,
        TK_M                = 0x10,
        TK_N                = 0x11,
        TK_O                = 0x12,
        TK_P                = 0x13,
        TK_Q                = 0x14,
        TK_R                = 0x15,
        TK_S                = 0x16,
        TK_T                = 0x17,
        TK_U                = 0x18,
        TK_V                = 0x19,
        TK_W                = 0x1A,
        TK_X                = 0x1B,
        TK_Y                = 0x1C,
        TK_Z                = 0x1D,
        TK_1                = 0x1E,
        TK_2                = 0x1F,
        TK_3                = 0x20,
        TK_4                = 0x21,
        TK_5                = 0x22,
        TK_6                = 0x23,
        TK_7                = 0x24,
        TK_8                = 0x25,
        TK_9                = 0x26,
        TK_0                = 0x27,
        TK_RETURN           = 0x28,
        TK_ENTER            = 0x28,
        TK_ESCAPE           = 0x29,
        TK_BACKSPACE        = 0x2A,
        TK_TAB              = 0x2B,
        TK_SPACE            = 0x2C,
        TK_MINUS            = 0x2D,
        TK_EQUALS           = 0x2E,
        TK_LBRACKET         = 0x2F,
        TK_RBRACKET         = 0x30,
        TK_BACKSLASH        = 0x31,
        TK_SEMICOLON        = 0x33,
        TK_APOSTROPHE       = 0x34,
        TK_GRAVE            = 0x35,
        TK_COMMA            = 0x36,
        TK_PERIOD           = 0x37,
        TK_SLASH            = 0x38,
        TK_F1               = 0x3A,
        TK_F2               = 0x3B,
        TK_F3               = 0x3C,
        TK_F4               = 0x3D,
        TK_F5               = 0x3E,
        TK_F6               = 0x3F,
        TK_F7               = 0x40,
        TK_F8               = 0x41,
        TK_F9               = 0x42,
        TK_F10              = 0x43,
        TK_F11              = 0x44,
        TK_F12              = 0x45,
        TK_PAUSE            = 0x48,
        TK_INSERT           = 0x49,
        TK_HOME             = 0x4A,
        TK_PAGEUP           = 0x4B,
        TK_DELETE           = 0x4C,
        TK_END              = 0x4D,
        TK_PAGEDOWN         = 0x4E,
        TK_RIGHT            = 0x4F,
        TK_LEFT             = 0x50,
        TK_DOWN             = 0x51,
        TK_UP               = 0x52,
        TK_KP_DIVIDE        = 0x54,
        TK_KP_MULTIPLY      = 0x55,
        TK_KP_MINUS         = 0x56,
        TK_KP_PLUS          = 0x57,
        TK_KP_ENTER         = 0x58,
        TK_KP_1             = 0x59,
        TK_KP_2             = 0x5A,
        TK_KP_3             = 0x5B,
        TK_KP_4             = 0x5C,
        TK_KP_5             = 0x5D,
        TK_KP_6             = 0x5E,
        TK_KP_7             = 0x5F,
        TK_KP_8             = 0x60,
        TK_KP_9             = 0x61,
        TK_KP_0             = 0x62,
        TK_KP_PERIOD        = 0x63,
        TK_SHIFT            = 0x70,
        TK_CONTROL          = 0x71,
    
        // Mouse events/states
        TK_MOUSE_LEFT       = 0x80, // Buttons
        TK_MOUSE_RIGHT      = 0x81,
        TK_MOUSE_MIDDLE     = 0x82,
        TK_MOUSE_X1         = 0x83,
        TK_MOUSE_X2         = 0x84,
        TK_MOUSE_MOVE       = 0x85, // Movement event
        TK_MOUSE_SCROLL     = 0x86, // Mouse scroll event
        TK_MOUSE_X          = 0x87, // Cusor position in cells
        TK_MOUSE_Y          = 0x88,
        TK_MOUSE_PIXEL_X    = 0x89, // Cursor position in pixels
        TK_MOUSE_PIXEL_Y    = 0x8A,
        TK_MOUSE_WHEEL      = 0x8B, // Scroll direction and amount
        TK_MOUSE_CLICKS     = 0x8C, // Number of consecutive clicks
    
        //If key was released instead of pressed, it's code will be OR'ed withTK_KEY_RELEASED
        TK_KEY_RELEASED     = 0x100,
    
        // Virtual key-codes for internal terminal states/variables.
        // These can be accessed via terminal_state function.
        TK_WIDTH            = 0xC0, // Terminal width in cells
        TK_HEIGHT           = 0xC1, // Terminal height in cells
        TK_CELL_WIDTH       = 0xC2, // Cell width in pixels
        TK_CELL_HEIGHT      = 0xC3, // Cell height in pixels
        TK_COLOR            = 0xC4, // Current foregroung color
        TK_BKCOLOR          = 0xC5, // Current background color
        TK_LAYER            = 0xC6, // Current layer
        TK_COMPOSITION      = 0xC7, // Current composition state
        TK_CHAR             = 0xC8, // Translated ANSI code of last produced character
        TK_WCHAR            = 0xC9, // Unicode codepoint of last produced character
        TK_EVENT            = 0xCA, // Last dequeued event
        TK_FULLSCREEN       = 0xCB, // Fullscreen state
    
        // Other events
        TK_CLOSE            = 0xE0,
        TK_RESIZED          = 0xE1,
    
        // Generic mode enum.
        // Right now it is used for composition option only.
        TK_OFF              =    0,
        TK_ON               =    1,
    
        // Input result codes for terminal_read function.
        TK_INPUT_NONE       =    0,
        TK_INPUT_CANCELLED  =   -1;

        [DllImport("BearLibTerminal.dll", EntryPoint = "terminal_open", CallingConvention=CallingConvention.Cdecl)]
        public static extern bool Open();

        [DllImport("BearLibTerminal.dll", EntryPoint = "terminal_close", CallingConvention = CallingConvention.Cdecl)]
        public static extern void Close();

        [DllImport("BearLibTerminal.dll", EntryPoint = "terminal_refresh", CallingConvention = CallingConvention.Cdecl)]
        public static extern void Refresh();

        [DllImport("BearLibTerminal.dll", CharSet = CharSet.Unicode, EntryPoint = "terminal_set16", CallingConvention = CallingConvention.Cdecl)]
        public static extern bool Set(string options);

        public static bool Set(string options, params object[] args)
        {
        	Dictionary<Bitmap, BitmapData> bitmaps = new Dictionary<Bitmap, BitmapData>();
        	for (int i=0; i<args.Length; i++)
        	{
        		if (args[i] is Bitmap)
        		{
        			Bitmap bitmap = args[i] as Bitmap;
        			BitmapData data = bitmap.LockBits
					(
						new Rectangle(0, 0, bitmap.Width, bitmap.Height),
						ImageLockMode.ReadOnly,
						PixelFormat.Format32bppArgb
					);
					bitmaps[bitmap] = data;
					args[i] = string.Format("0x{0:X}", (System.UInt64)data.Scan0.ToInt64());
        		}
        	}
        	bool result = Set(string.Format(options, args));
        	foreach(KeyValuePair<Bitmap, BitmapData> i in bitmaps)
        	{
        		i.Key.UnlockBits(i.Value);
        	}
            return result;
        }

        [DllImport("BearLibTerminal.dll", EntryPoint = "terminal_clear", CallingConvention = CallingConvention.Cdecl)]
        public static extern void Clear();

        [DllImport("BearLibTerminal.dll", EntryPoint = "terminal_clear_area", CallingConvention = CallingConvention.Cdecl)]
        public static extern void ClearArea(int x, int y, int w, int h);
        
        [DllImport("BearLibTerminal.dll", EntryPoint = "terminal_crop", CallingConvention = CallingConvention.Cdecl)]
        public static extern void Crop(int x, int y, int w, int h);

        [DllImport("BearLibTerminal.dll", EntryPoint = "terminal_color", CallingConvention = CallingConvention.Cdecl)]
        private static extern void ColorImpl(int argb);

        public static void Color(Color color)
        {
            ColorImpl(color.ToArgb());
        }
        
        public static void Color(string name)
        {
        	ColorImpl(ColorFromNameImpl(name));
        }

        [DllImport("BearLibTerminal.dll", EntryPoint = "terminal_bkcolor", CallingConvention=CallingConvention.Cdecl)]
        private static extern void BkColorImpl(int argb);

        public static void BkColor(Color color)
        {
            BkColorImpl(color.ToArgb());
        }
        
        public static void BkColor(string name)
        {
        	BkColorImpl(ColorFromNameImpl(name));
        }

        [DllImport("BearLibTerminal.dll", EntryPoint = "terminal_composition", CallingConvention = CallingConvention.Cdecl)]
        public static extern void Composition(int mode);
        
        [DllImport("BearLibTerminal.dll", EntryPoint = "terminal_layer", CallingConvention = CallingConvention.Cdecl)]
        public static extern void Layer(int mode);

        [DllImport("BearLibTerminal.dll", EntryPoint = "terminal_put", CallingConvention = CallingConvention.Cdecl)]
        public static extern void Put(int x, int y, int code);

        public static void Put(int x, int y, char code)
        {
            Put(x, y, (int)code);
        }
        
        [DllImport("BearLibTerminal.dll", EntryPoint = "terminal_put_ext", CallingConvention = CallingConvention.Cdecl)]
        private static extern void PutExtImpl(int x, int y, int dx, int dy, int code, [MarshalAs(UnmanagedType.LPArray)]System.Int32[] corners);
        
        public static void PutExt(int x, int y, int dx, int dy, int code)
        {
        	PutExtImpl(x, y, dx, dy, code, null);
        }
        
        public static void PutExt(int x, int y, int dx, int dy, char code)
        {
        	PutExt(x, y, dx, dy, (int)code);
        }
        
        public static void PutExt(int x, int y, int dx, int dy, int code, Color[] corners)
        {
        	System.Int32[] values = new System.Int32[4];
        	for (int i=0; i<4; i++) values[i] = corners[i].ToArgb();
        	PutExtImpl(x, y, dx, dy, code, values);
        }
        
        public static void PutExt(int x, int y, int dx, int dy, char code, Color[] corners)
        {
        	PutExt(x, y, dx, dy, (int)code, corners);
        }

        [DllImport("BearLibTerminal.dll", CharSet = CharSet.Unicode, EntryPoint = "terminal_print16", CallingConvention = CallingConvention.Cdecl)]
        public static extern int Print(int x, int y, string text);

        public static int Print(int x, int y, string text, params object[] args)
        {
            return Print(x, y, string.Format(text, args));
        }
        
        [DllImport("BearLibTerminal.dll", CharSet = CharSet.Unicode, EntryPoint = "terminal_measure16", CallingConvention = CallingConvention.Cdecl)]
        public static extern int Measure(string text);

        public static int Measure(string text, params object[] args)
        {
            return Measure(string.Format(text, args));
        }

        [DllImport("BearLibTerminal.dll", EntryPoint = "terminal_has_input", CallingConvention = CallingConvention.Cdecl)]
        public static extern bool HasInput();
        
        [DllImport("BearLibTerminal.dll", EntryPoint = "terminal_state", CallingConvention = CallingConvention.Cdecl)]
        public static extern int State(int code);
        
        public static bool Check(int code)
        {
            return State(code) > 0;
        }

        [DllImport("BearLibTerminal.dll", EntryPoint = "terminal_read", CallingConvention = CallingConvention.Cdecl)]
        public static extern int Read();

        [DllImport("BearLibTerminal.dll", CharSet = CharSet.Unicode, EntryPoint = "terminal_read_str16", CallingConvention = CallingConvention.Cdecl)]
        public static extern int ReadStr(int x, int y, StringBuilder text, int max);

        public static int ReadStr(int x, int y, ref string text, int max)
        {
            StringBuilder buffer = new StringBuilder(text, max);
            int result = ReadStr(x, y, buffer, max);
            text = buffer.ToString();
            return result;
        }

        [DllImport("BearLibTerminal.dll", CharSet = CharSet.Unicode, EntryPoint = "color_from_name16", CallingConvention=CallingConvention.Cdecl)]
        private static extern int ColorFromNameImpl(string name);

        public static Color ColorFromName(string name)
        {
            return System.Drawing.Color.FromArgb(ColorFromNameImpl(name));
        }
    }
}
