/*
 * Copyright (C) 2015 Ben Hamilton
 *
 * Originally from JNA com.sun.jna.platform.win32 package (Apache
 * License, Version 2.0)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.zaxxer.nuprocess.windows;

import java.util.Arrays;
import java.util.List;

import com.sun.jna.*;
import com.sun.jna.ptr.ByReference;
import com.sun.jna.ptr.ByteByReference;

/**
 * Constants and structures for Windows APIs, borrowed from com.sun.jna.platform.win32
 * to avoid pulling in a dependency on that package.
 */
@SuppressWarnings("serial")
public interface NuWinNT
{
   int CREATE_SUSPENDED = 0x00000004;
   int CREATE_UNICODE_ENVIRONMENT = 0x00000400;
   int CREATE_NO_WINDOW = 0x08000000;

   int ERROR_SUCCESS = 0;
   int ERROR_BROKEN_PIPE = 109;
   int ERROR_PIPE_NOT_CONNECTED = 233;
   int ERROR_PIPE_CONNECTED = 535;
   int ERROR_IO_PENDING = 997;

   int FILE_ATTRIBUTE_NORMAL = 0x00000080;
   int FILE_FLAG_OVERLAPPED = 0x40000000;
   int FILE_SHARE_READ = 0x00000001;
   int FILE_SHARE_WRITE = 0x00000002;

   int GENERIC_READ = 0x80000000;
   int GENERIC_WRITE = 0x40000000;

   int OPEN_EXISTING = 3;

   int STATUS_PENDING = 0x00000103;
   int STILL_ACTIVE = STATUS_PENDING;

   int STARTF_USESTDHANDLES = 0x100;

   HANDLE INVALID_HANDLE_VALUE = new HANDLE(HANDLE.INVALID);

   class HANDLE extends PointerType
   {
      static final Pointer INVALID = Pointer.createConstant(Native.POINTER_SIZE == 8 ? -1 : 0xFFFFFFFFL);

      public HANDLE()
      {
      }

      public HANDLE(Pointer p)
      {
         setPointer(p);
      }

      @Override
      public Object fromNative(Object nativeValue, FromNativeContext context)
      {
         if (nativeValue == null) {
            return null;
         }

         Pointer ptr = (Pointer) nativeValue;
         if (INVALID.equals(ptr)) {
            return INVALID_HANDLE_VALUE;
         }
         return new HANDLE(ptr);
      }
   }

   class WORD extends IntegerType
   {
      public static final int SIZE = 2;

      public WORD()
      {
         this(0);
      }

      public WORD(long value)
      {
         super(SIZE, value, true);
      }
   }

   class DWORD extends IntegerType
   {
      public static final int SIZE = 4;

      public DWORD()
      {
         this(0);
      }

      public DWORD(long value)
      {
         super(SIZE, value, true);
      }
   }

   class ULONG_PTR extends IntegerType
   {
      public ULONG_PTR()
      {
         this(0);
      }

      public ULONG_PTR(long value)
      {
         super(Native.POINTER_SIZE, value, true);
      }
   }

   class ULONG_PTRByReference extends ByReference
   {
      public ULONG_PTRByReference()
      {
         this(new ULONG_PTR(0));
      }

      public ULONG_PTRByReference(ULONG_PTR value)
      {
         super(Native.POINTER_SIZE);
         setValue(value);
      }

      public void setValue(ULONG_PTR value)
      {
         if (Native.POINTER_SIZE == 4) {
            getPointer().setInt(0, value.intValue());
         }
         else {
            getPointer().setLong(0, value.longValue());
         }
      }

      public ULONG_PTR getValue()
      {
         return new ULONG_PTR(Native.POINTER_SIZE == 4 ? getPointer().getInt(0) : getPointer().getLong(0));
      }
   }

   class SECURITY_ATTRIBUTES extends Structure
   {
      public DWORD dwLength;
      public Pointer lpSecurityDescriptor;
      public boolean bInheritHandle;

      @Override
      protected List<String> getFieldOrder()
      {
         return Arrays.asList("dwLength", "lpSecurityDescriptor", "bInheritHandle");
      }
   }

   class STARTUPINFO extends Structure
   {
      public DWORD cb;
      public String lpReserved;
      public String lpDesktop;
      public String lpTitle;
      public DWORD dwX;
      public DWORD dwY;
      public DWORD dwXSize;
      public DWORD dwYSize;
      public DWORD dwXCountChars;
      public DWORD dwYCountChars;
      public DWORD dwFillAttribute;
      public int dwFlags;
      public WORD wShowWindow;
      public WORD cbReserved2;
      public ByteByReference lpReserved2;
      public HANDLE hStdInput;
      public HANDLE hStdOutput;
      public HANDLE hStdError;

      public STARTUPINFO()
      {
         cb = new DWORD(size());
      }

      @Override
      protected List<String> getFieldOrder()
      {
         return Arrays.asList("cb", "lpReserved", "lpDesktop", "lpTitle", "dwX", "dwY", "dwXSize", "dwYSize",
               "dwXCountChars", "dwYCountChars", "dwFillAttribute", "dwFlags", "wShowWindow", "cbReserved2",
               "lpReserved2", "hStdInput", "hStdOutput", "hStdError");
      }
   }

   class PROCESS_INFORMATION extends Structure
   {
      public HANDLE hProcess;
      public HANDLE hThread;
      public DWORD dwProcessId;
      public DWORD dwThreadId;

      @Override
      protected List<String> getFieldOrder()
      {
         return Arrays.asList("hProcess", "hThread", "dwProcessId", "dwThreadId");
      }
   }
}
