/*

CLASS {NATIVE_Zlib|_C_Shapr},
UNTESTED, BYTES, NATIVE:

IMPORT: {Stream} .

NATIVE: "///COPY NATIVE_Zlib.cs" .
*/

using AL4;
using AL4_project;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;

namespace AL4_project
{

public class NATIVE_Zlib
{

/*

//FUNCTION Create_zlib_decompress|_stream(
//{Stream} Out|put_stream) ==> INT RESULT|_handle, NATIVE :
//"	RESULT = NATIVE_Zlib.Do_create_zlib_decompress(X_Out);" .

FUNCTION Create_zlib_decompress_in|put_stream(
{Stream} In|put_stream) ==> INT RESULT|_handle, NATIVE :
"	RESULT = NATIVE_Zlib.Do_create_zlib_decompress_in(X_In);" .

FUNCTION Create_zlib_compress|_stream(
{Stream} Out|put_stream,
INT Level|_compression) ==> INT RESULT|_handle, NATIVE :
"	RESULT = NATIVE_Zlib.Do_create_zlib_compress(X_Out, (int)X_Level);" .

FUNCTION Zlib_output(INT Handle|_of_zlib,
BYTE Source|_array[*]), NATIVE :
	CASE Source[].Count == 0 ? ==> ;
	NATIVE @
"	NATIVE_Zlib.Do_output((int)X_Handle, X_Source);" .

FUNCTION Zlib_input|_decompress(INT Handle|_of_zlib,
BYTE Source|_array[*]) ==> INT, NATIVE :
	CASE Source[].Count == 0 ? ==> ;
	NATIVE @
"	RESULT = NATIVE_Zlib.Do_input((int)X_Handle, X_Source);" .

FUNCTION Zlib_stream_close(INT Handle|_of_zlib), NATIVE :
"	NATIVE_Zlib.Do_zlib_close((int)X_Handle);" .

FUNCTION Zlib_native_decompress_to_file(
INT Handle|_of_zlib, STR Dest|ination_file_path,
INT Len|gth_not_compressed) ==> BOOL, NATIVE :
"	RESULT = NATIVE_Zlib.Do_decompress_to_file("
"		(int)X_Handle, X_Dest, (int)X_Len);" .

FUNCTION Zlib_header_length ==> INT := 0 .


*/

	public static List<NATIVE_Zlib> All_zlib = new List<NATIVE_Zlib>();
	
	public AL4_write a_out;
	public DeflateStream z_out;
	public AL4_read a_in;
	public DeflateStream z_in;
	public int handle;
	
	public static int allocate_handle(NATIVE_Zlib nz)
	{
		if (All_zlib.Count == 0)
			All_zlib.Add(new NATIVE_Zlib());
		int result = All_zlib.IndexOf(null);
		if (result < 0)
		{
			result = All_zlib.Count;
			All_zlib.Add(null);
		}
		All_zlib[result] = nz;
		nz.handle = result;
		return result;
	}

    //public static int Do_create_zlib_decompress(Ref_obj X_Out)
    //{
    //    NATIVE_Zlib nz = new NATIVE_Zlib();
    //    nz.a_out = new AL4_write(X_Out);
    //    nz.z_out = new DeflateStream(nz.a_out, CompressionMode.Decompress, true);
    //    return allocate_handle(nz);
    //}
	
    public static int Do_create_zlib_decompress_in(Ref_obj X_In)
    {
        NATIVE_Zlib nz = new NATIVE_Zlib();
        nz.a_in = new AL4_read(X_In);
        nz.z_in = new DeflateStream(nz.a_in, CompressionMode.Decompress, false);
        return allocate_handle(nz);
    }
	
	public static int Do_create_zlib_compress(Ref_obj X_Out, int X_Level_ignore)
	{
		NATIVE_Zlib nz = new NATIVE_Zlib();
		nz.a_out = new AL4_write(X_Out);
        //System.IO.Compression.CompressionLevel cl
        //    = System.IO.Compression.CompressionLevel.Optimal;
        //if (X_Level == (int)System.IO.Compression.CompressionLevel.Fastest ||
        //    X_Level == (int)System.IO.Compression.CompressionLevel.Optimal ||
        //    X_Level == (int)System.IO.Compression.CompressionLevel.NoCompression)
        //    cl = (System.IO.Compression.CompressionLevel)X_Level;
        //nz.z_out = new DeflateStream(nz.a_out, cl);
        nz.z_out = new DeflateStream(nz.a_out, CompressionMode.Compress);
		return allocate_handle(nz);
	}
	
	~NATIVE_Zlib()
	{
		if (!closed)
			Do_zlib_close(handle);
		if (handle <= 0) return;
		All_zlib[handle] = null;
	}
	
	bool closed;
	
	public static void Do_zlib_close(int X_Handle)
	{
		NATIVE_Zlib nz = All_zlib[X_Handle];
		if (nz.closed) return;
		if (nz.z_out != null)
		{
            //nz.a_out.Flush();
            //nz.a_out.Close();
            //nz.z_out.Flush();
            //nz.z_out.Close();
			nz.z_out = null;
			nz.a_out = null;
		}
		if (nz.z_in != null)
		{
            //nz.a_in.Close();
            //nz.z_in.Close();
            nz.a_in = null;
			nz.z_in = null;
		}
		nz.closed = true;
	}
	
    public static int Do_input(int X_Handle, ArrayByte X_Dest)
    {
        NATIVE_Zlib nz = All_zlib[X_Handle];
		
        if (nz.z_in == null) return 0;
        byte[] data = new byte[X_Dest.Count];
        int result = nz.z_in.Read(data, 0, X_Dest.Count);
        for (int i = 0; i < result; i ++)
        	X_Dest.set_byte(i, data[i]);
        return result;
    }

	public static void Do_output(int X_Handle, ArrayByte X_Source)
	{
		NATIVE_Zlib nz = All_zlib[X_Handle];
		
		if (nz.z_out == null) return;
		byte[] data = new byte[X_Source.Count];
		for (int i =0; i < X_Source.Count; i ++)
		{
			data[i] = X_Source.get_byte(i);
		}
		nz.z_out.Write(data, 0, data.Length);
	}
	
	public static bool Do_decompress_to_file(int X_Handle, string X_Dest, int X_Len)
	{
		NATIVE_Zlib nz = All_zlib[X_Handle];
		
		if (nz.z_in == null) return false;
		bool ok = false;
		using(FileStream dest = File.Create(X_Dest))
		{
			long was_pos = dest.Position;
			nz.z_in.CopyTo(dest);
			ok = dest.Position - was_pos == (long)X_Len; 
		}
		return ok;
	}

}

public class AL4_read : System.IO.Stream
{
    public Ref_obj AL4_stream; // ref to C_Stream
	
    public AL4_read(Ref_obj al4_stream) : base()
    {
        AL4_stream = C_Stream.NoneObj_C_Stream.hard_link;
		AL4_object.Object_assign_hard(ref AL4_stream, al4_stream.Obj);
    }
    
    ~AL4_read()
	{
		AL4_stream.Ref_dec_hard();
	}
	
    public override bool CanRead { get { return true; }}
    public override bool CanWrite { get { return false; }}
    public override bool CanSeek { get { return false; }}
    public override long Length {
        get { 
        	Ref_obj ref1 = C_Long_int.St_long.New();
            AL4_object.Object_assign_hard(ref ref1, 
            	((C_Stream)(AL4_stream.Obj)).Do.M_Size().Obj); 
        	C_Long_int.St_long r = (C_Long_int.St_long)ref1.Obj;
        	long result = r.f_Lo | (r.f_Hi << 32);
        	return result; 
        	//(long)((C_Stream)(AL4_stream.Obj)).Do.M_Size(); 
        }
    }
    public override long Position {
        get { 
			Ref_obj ref1 = C_Long_int.St_long.New();
            AL4_object.Object_assign_hard(ref ref1, 
            	((C_Stream)(AL4_stream.Obj)).Do.M_Position().Obj); 
        	C_Long_int.St_long r = (C_Long_int.St_long)ref1.Obj;
        	long result = r.f_Lo | (r.f_Hi << 32);        	
        	return result; 
        	//return (long)((C_Stream)(AL4_stream.Obj)).Do.M_Position(); 
    	}
        set { }
    }
	
    public override void Flush() {}
    public override long Seek(long where, SeekOrigin origin) { return 0L; }
    public override void SetLength(long value) {}
    public override void Write(byte[] what, int offset, int count) {}
    public override int Read(byte[] what, int offset, int count)
    {
        DynAByte xdest = FixAByte_OnArray.new_FixAByte_OnArray(
            null, what);
        ArrayByte final_dest = xdest;
        if (offset > 0)
            final_dest = FixAByte
            .new_FixAByte(null, xdest, offset, offset + count - 1);
        int result = (int)
        	((C_Stream)(AL4_stream.Obj)).Do.M_Read_bytes(final_dest);
		
        if (final_dest == xdest) {
            final_dest = null;
            xdest.Dispose();
        } else {
            final_dest.Dispose();
            xdest.Dispose();
        }
        return result;
    }

}

public class AL4_write : System.IO.Stream
{
	public Ref_obj AL4_stream; // ref to C_Stream
	Stream stream_out = null;
	
	public AL4_write(Ref_obj al4_stream) : base()
	{
		AL4_stream = C_Stream.NoneObj_C_Stream.hard_link;
		AL4_object.Object_assign_hard(ref AL4_stream, al4_stream.Obj);
		if (((C_Stream)AL4_stream.Obj) is C_File_stream)
            {
                stream_out = NATIVE_File_stream.All_streams[(int)
                    ((C_File_stream)AL4_stream.Obj).F_Handle];
            }
	}
	
	~AL4_write()
	{
		AL4_stream.Ref_dec_hard();
	}
	
	public override bool CanWrite { get { return true; }}
	public override bool CanRead { get { return false; }}
	public override bool CanSeek { get { return false; }}
	public override long Length { 
		get {  
        	Ref_obj ref1 = C_Long_int.St_long.New();
            AL4_object.Object_assign_hard(ref ref1, 
            	((C_Stream)(AL4_stream.Obj)).Do.M_Size().Obj); 
        	C_Long_int.St_long r = (C_Long_int.St_long)ref1.Obj;
        	long result = r.f_Lo | (r.f_Hi << 32);
        	return result; 		
		}
	}
	public override long Position { 
		get { 
			Ref_obj ref1 = C_Long_int.St_long.New();
            AL4_object.Object_assign_hard(ref ref1, 
            	((C_Stream)(AL4_stream.Obj)).Do.M_Position().Obj); 
        	C_Long_int.St_long r = (C_Long_int.St_long)ref1.Obj;
        	long result = r.f_Lo | (r.f_Hi << 32);
        	return result;  
		}
		set { }
	}
	
	public override void Flush() {}
	public override long Seek(long where, SeekOrigin origin) { return 0L; }
	public override void SetLength(long value) {}
	public override void Write(byte[] what, int offset, int count) 
	{
		if (stream_out != null)
		{
			stream_out.Write(what, offset, count);
			return;
		}
		DynAByte xdest = FixAByte_OnArray.new_FixAByte_OnArray(
			null, what);
		ArrayByte final_dest = xdest;
		if (offset > 0 || count < what.Length) 
			final_dest = FixAByte
			.new_FixAByte(null, xdest, offset, offset + count - 1);
		int result = (int)
			((C_Stream)(AL4_stream.Obj)).Do.M_Save_bytes(final_dest);
		
		if (final_dest == xdest) { 
			final_dest = null;
			xdest.Dispose();
		} else {
			final_dest.Dispose();
			xdest.Dispose();
		}
	}
	public override void Close ()
	{
		((C_Stream)(AL4_stream.Obj)).Do.M_Close();
	}
	
	public override int Read(byte[] what, int offset, int count) 
	{ return 0; }

}

}

/*

END

*/
