/*

CLASS {NATIVE_Scan_cat|alog_directory|CSharp}, 
UNTESTED, NATIVE, BITWISE:

IMPORT: {String_functions}, {System_functions} . //, {Hardware} .
IMPORT, ANYWAY: {Date_time}, {File_path} .

NATIVE: "///COPY NATIVE_Scan_cat.cs" .

*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;

using AL4;

namespace AL4
{
   public class NATIVE_Scan_cat
   {
   
   	public enum FINDEX_INFO_LEVELS {
	    FindExInfoStandard=0,
	    FindExInfoBasic=1
	}
	
	public enum FINDEX_SEARCH_OPS {
	     FindExSearchNameMatch = 0,
	     FindExSearchLimitToDirectories = 1,
	     FindExSearchLimitToDevices = 2
	}
	
	public const int FIND_FIRST_EX_CASE_SENSITIVE= 1;
	public const int FIND_FIRST_EX_LARGE_FETCH = 2;

   	[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
   	public static extern IntPtr FindFirstFileEx(
	        string lpFileName,
	        FINDEX_INFO_LEVELS fInfoLevelId,
	        out WIN32_FIND_DATA lpFindFileData,
	        FINDEX_SEARCH_OPS fSearchOp,
	        IntPtr lpSearchFilter,
	        int dwAdditionalFlags);
	
	[DllImport("kernel32.dll", CharSet=CharSet.Unicode)]
	public static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA
	   	lpFindFileData);
	   	                        
	[DllImport("kernel32.dll")]
	public static extern bool FindClose(IntPtr hFindFile);	   	
	   	
	[StructLayout(LayoutKind.Sequential)]
	public struct FILETIME {
	    public uint dwLowDateTime;
	    public uint dwHighDateTime;
	}	   	
	
	[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
	public struct WIN32_FIND_DATA
	{
	    public uint dwFileAttributes;
	    public FILETIME ftCreationTime;
	    public FILETIME ftLastAccessTime;
	    public FILETIME ftLastWriteTime;
	    public uint nFileSizeHigh;
	    public uint nFileSizeLow;
	    public uint dwReserved0;
	    public uint dwReserved1;
	    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=260)]
	    public string cFileName;
	    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=14)]
	    public string cAlternateFileName;
	    public uint dwFileType;
	    public uint dwCreatorType;
	    public uint wFinderFlags;
	}   
	
	[StructLayout(LayoutKind.Sequential)]
	public struct SYSTEMTIME {
	  [MarshalAs(UnmanagedType.U2)] public short Year;
	  [MarshalAs(UnmanagedType.U2)] public short Month;
	  [MarshalAs(UnmanagedType.U2)] public short DayOfWeek;
	  [MarshalAs(UnmanagedType.U2)] public short Day;
	  [MarshalAs(UnmanagedType.U2)] public short Hour;
	  [MarshalAs(UnmanagedType.U2)] public short Minute;
	  [MarshalAs(UnmanagedType.U2)] public short Second;
	  [MarshalAs(UnmanagedType.U2)] public short Milliseconds;
	
	  public SYSTEMTIME( DateTime dt )
	  {
	    dt = dt.ToUniversalTime();  // SetSystemTime expects the SYSTEMTIME in UTC
	    Year = (short)dt.Year;
	    Month = (short)dt.Month;
	    DayOfWeek = (short)dt.DayOfWeek;
	    Day = (short)dt.Day;
	    Hour = (short)dt.Hour;
	    Minute = (short)dt.Minute;
	    Second = (short)dt.Second;
	    Milliseconds = (short)dt.Millisecond;
	  }
	}	
	
	[DllImport("kernel32.dll")]
	public static extern bool FileTimeToLocalFileTime(
		[In] ref FILETIME lpFileTime,
		out FILETIME lpLocalFileTime);
	   
	public static DateTime ConvertFileTime(FILETIME ft)
	{
	    long time = ((long)ft.dwHighDateTime << 32) + ft.dwLowDateTime;
	    var dto = DateTimeOffset.FromFileTime(time).ToLocalTime();
	    return dto.UtcDateTime;
	}	   
	
	[DllImport("kernel32.dll",
	        CallingConvention=CallingConvention.Winapi,
	        SetLastError=true)]
	public static extern bool FileTimeToSystemTime([In] ref FILETIME lpFileTime,
	   out SYSTEMTIME lpSystemTime);	
   
   	public static List<IntPtr> All_scans = new List<IntPtr>();
   	//----------------------------------------------------------------
   	[DllImport("mpr.dll")]
    public static extern int WNetGetUniversalNameA(
        string lpLocalPath, int dwInfoLevel, IntPtr lpBuffer, ref int lpBufferSize
    );

    // I think max length for UNC is actually 32,767
    public static string LocalToUNC(string localPath, int maxLen = 2000)
    {
        IntPtr lpBuff;

        // Allocate the memory
        try
        {
            lpBuff = Marshal.AllocHGlobal(maxLen); 
        }
        catch (OutOfMemoryException)
        {
            return null;
        }

        try
        {
            int res = WNetGetUniversalNameA(localPath, 1, lpBuff, ref maxLen);

            if (res != 0)
                return null;

            // lpbuff is a structure, whose first element is a pointer to the UNC name (just going to be lpBuff + sizeof(int))
            return Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(lpBuff));
        }
        catch (Exception)
        {
            return null;
        }
        finally
        {
            Marshal.FreeHGlobal(lpBuff);
        }
    }
   }
}
/*

NATIVE: @
	"using System;"
	"using System.Collections.Generic;"
	"using System.Linq;"
	"using System.Text;"
	"using System.IO;"
	"using System.Runtime.InteropServices;" .

FUNCTION Create_scan_cat(
STR Path|_to_scan,
STR Items|_names[],
{file_detail} Details|_corresponding[]) ==> INT
	:
	RESULT = do_create_scan_cat(Path, Items[])
	CASE Items[].Count==1 ? 
	     Details[] << File_detail(Path.Trailing_slash Items[0])
	     CASE Details[*].Attrib & DIRECTORY != 0 ? Items[*] = _.Trailing_slash ;
         //DEBUG: "first file is " Items[0]
         //       ", attr=" Details[0].Attrib.Hex2
         //       ", RESULT=" RESULT #NL ;
	; .
	
FUN do_create_scan_cat(
STR Path|_to_scan,
STR Items|_names[]) ==> INT, NATIVE :
	STR ex_path|_wildcarded = Path.Trailing_slash "*"
	NATIVE @
	"if (NATIVE_Scan_cat.All_scans.Count() == 0)"
    "   NATIVE_Scan_cat.All_scans.Add(IntPtr.Zero);"
    "int h = NATIVE_Scan_cat.All_scans.LastIndexOf(IntPtr.Zero);"
    "if (h <= 0)"
    "{"
    "   h = NATIVE_Scan_cat.All_scans.Count();"
    "   NATIVE_Scan_cat.All_scans.Add(IntPtr.Zero);"
    "}"
    ""     
	"string unc_path = NATIVE_Scan_cat.LocalToUNC(X_ex_path);"
   //''Console.Write("unc_path=" + unc_path + "\n");''
	"if (unc_path == "#QU#QU" || unc_path==null) unc_path = X_ex_path;"
	""     
    "NATIVE_Scan_cat.FINDEX_INFO_LEVELS fLevel "
    "	= NATIVE_Scan_cat.FINDEX_INFO_LEVELS.FindExInfoStandard;"
    "int additional_flags = 0;"     
	"if (unc_path != X_ex_path && Environment.OSVersion.Version.Major >= 6) {"
	"		fLevel = NATIVE_Scan_cat.FINDEX_INFO_LEVELS.FindExInfoBasic;"
	"		additional_flags = NATIVE_Scan_cat.FIND_FIRST_EX_LARGE_FETCH;"
	"}"
    ""
    "NATIVE_Scan_cat.WIN32_FIND_DATA findData;"     
    ""     
    ""     
    "IntPtr scan = NATIVE_Scan_cat.FindFirstFileEx("
    "	X_ex_path, fLevel, out findData, "
    "	NATIVE_Scan_cat.FINDEX_SEARCH_OPS.FindExSearchNameMatch,"
    "	IntPtr.Zero, additional_flags);"
    ""
    "if (scan.ToInt32() == -1) return -1;"   
    ""
   //''Console.WriteLine("after FindFirstEx: " + findData.cFileName);''
    "X_Items.Add(findData.cFileName);"
    ""
    "NATIVE_Scan_cat.All_scans[h] = scan;"
    "RESULT = h;" .
    
FUNCTION Scan_cat(
INT Handle|_of_scan_cat,
STR Items|_names[],
{file_detail} Details|_corresponding[]) ==> BOOL RESULT|_scanning_finished
	:
    //DEBUG: "Scan_cat called, Handle=" Handle #NL ;
	CASE Handle < 0 ? RESULT = TRUE ==> ;
	INT attr|ibutes[]
	REAL size_dates[]
	STR filename = do_scan_cat(Handle, attr[], size_dates[]) 
	RESULT = filename == ""
	CASE RESULT ? 
		 //CASE Handle >= 0 ? Release_scan_handle(Handle) ;
		 ==> ;
	
	Details[] << {File_path}.{file_detail}(
		Attributes= attr[0],
		Size= size_dates[0],
		Creation= size_dates[1], 
		Modified= size_dates[2], 
		Accessed= size_dates[3])  
		   
	Items[] << filename if(Details[*].Attrib & DIRECTORY <> 0, Path_sep)
	.
	
FUN Release_scan_handle(INT Handle|_scanner), NATIVE :
	CASE Handle < 0 ? ==> ;
NATIVE @
	"IntPtr h = NATIVE_Scan_cat.All_scans[(int)X_Handle];"
	"NATIVE_Scan_cat.FindClose(h);"
    "NATIVE_Scan_cat.All_scans[(int)X_Handle] = IntPtr.Zero;" .
	
FUN do_scan_cat(
INT Handle|_scanner, INT attr|ibutes[], REAL size_dates[]) ==> STR, NATIVE : @
	"IntPtr scan = NATIVE_Scan_cat.All_scans[(int)X_Handle];"
	""
    "NATIVE_Scan_cat.WIN32_FIND_DATA findData;"
    ""
	"if (!NATIVE_Scan_cat.FindNextFile(scan, out findData)) {"
    //"	NATIVE_Scan_cat.FindClose(scan);"
    //"	NATIVE_Scan_cat.All_scans[(int)X_Handle] = IntPtr.Zero;"
	"	return RESULT;"     
	"}"     
	""
	"RESULT = findData.cFileName;"
	""     
	"X_attr.Add(findData.dwFileAttributes);"
	""
	"double sz = 0;"
	"if ((findData.dwFileAttributes & (uint)FileAttributes.Directory) == 0)"
	"       sz = findData.nFileSizeLow + findData.nFileSizeHigh * 4294967296.0 ;"
	"X_size_dates.Add(sz);"
	""
    "NATIVE_Scan_cat.FILETIME LT;"
    "DateTime dt;"
    "double q = 24.0 * 3600000;"
    ""
	"NATIVE_Scan_cat.FileTimeToLocalFileTime("
	"	ref findData.ftCreationTime, out LT);"
	"dt = NATIVE_Scan_cat.ConvertFileTime(LT);"
	"X_size_dates.Add(dt.ToOADate() * q);"     
	""
	"NATIVE_Scan_cat.FileTimeToLocalFileTime("
	"	ref findData.ftLastWriteTime, out LT);"
	"dt = NATIVE_Scan_cat.ConvertFileTime(LT);"
	"X_size_dates.Add(dt.ToOADate() * q);"     
	""
	"NATIVE_Scan_cat.FileTimeToLocalFileTime("
	"	ref findData.ftLastAccessTime, out LT);"
	"dt = NATIVE_Scan_cat.ConvertFileTime(LT);"
	"X_size_dates.Add(dt.ToOADate() * q);"     
	.    

END

*/
