
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using Tesseract.Internal;
using Tesseract.Interop;

namespace Tesseract
{
	
	
	
	public class PixArray : DisposableBase, IEnumerable<Pix>
	{
		#region Static Constructors
		
		
		
		
		
		
		public static PixArray LoadMultiPageTiffFromFile(string filename)
		{			
			var	pixaHandle = LeptonicaApi.Native.pixaReadMultipageTiff( filename );
			if(pixaHandle == IntPtr.Zero)
			{
				throw new IOException($"Failed to load image '{filename}'.");
			}
			
			return new PixArray(pixaHandle);
		}
		
		#endregion
		
		#region Enumerator implementation
		
		
		
		
		private class PixArrayEnumerator : DisposableBase, IEnumerator<Pix>
		{
			#region Fields
			
			private readonly PixArray array;
			private readonly Pix[] items;
			private Pix current;
			private int index;
			private readonly int version;
			
			#endregion
			
			#region Constructor
			
			public PixArrayEnumerator(PixArray array)
			{
				this.array = array;
				version = array.version;
				items = new Pix[array.Count];
				index = 0;
				current = null;
			}
		
			#endregion
			
			#region Enumerator Implementation
						
			
			public bool MoveNext()
			{
				VerifyArrayUnchanged();
				VerifyNotDisposed();
				
				if(index < items.Length) {
					if(items[index] == null) {
						items[index] = array.GetPix(index);
					}
					current = items[index];
					index++;
					return true;
				}

			    index = items.Length + 1;
			    current = null;
			    return false;
			}
						
			
			public Pix Current {
				get {
					VerifyArrayUnchanged();
					VerifyNotDisposed();
										
					return current;
				}
			}
			
			
			
			
			void IEnumerator.Reset()
			{
				VerifyArrayUnchanged();
				VerifyNotDisposed();
				
				index = 0;
				current = null;
			}
			
			
			object IEnumerator.Current {
				get {
					
					if(index == 0 || index == items.Length + 1) {
						throw new InvalidOperationException("The enumerator is positioned either before the first item or after the last item .");
					}
					
					return Current;
				}
			}
			
			
			
			
			private void VerifyArrayUnchanged()
			{				
				if(version != array.version) {
					throw new InvalidOperationException("PixArray was modified; enumeration operation may not execute.");
				}
			}
			
			#endregion	
			
			#region Disposal
			
			protected override void Dispose(bool disposing)
			{
				if(disposing) {
					for (int i = 0; i < items.Length; i++) {
						if(items[i] != null) {
							items[i].Dispose();
							items[i] = null;
						}
					}
				}
			}
			
			#endregion		
			
		}
		
		#endregion
		
		#region Fields
		
		
		
		
		private HandleRef _handle;
		private int _count;
		private int version;
		
		#endregion
		
		#region Constructor
		
		private PixArray(IntPtr handle)
		{
			_handle = new HandleRef(this, handle);
			version = 1;
			
			
			_count = LeptonicaApi.Native.pixaGetCount(_handle);
		}
		
		#endregion
		
		#region Properties
		
		
		
		
		public int Count
		{
			get {
				VerifyNotDisposed();
				return _count; 
			}
		}
		
		#endregion
		
		#region Methods
		
		
		
		
		
		
		
		public Pix GetPix(int index, PixArrayAccessType accessType = PixArrayAccessType.Clone)
		{
			Guard.Require("accessType", accessType == PixArrayAccessType.Clone || accessType == PixArrayAccessType.Copy, "Access type must be either copy or clone but was {0}.", accessType);
			Guard.Require("index", index >= 0 && index < Count, "The index {0} must be between 0 and {1}.", index, Count);
			
			VerifyNotDisposed();
			
			var pixHandle = LeptonicaApi.Native.pixaGetPix(_handle, index, accessType);
			if(pixHandle == IntPtr.Zero) {
				throw new InvalidOperationException($"Failed to retrieve pix {pixHandle}.");
			}
			return Pix.Create(pixHandle);
		}

		
		
		
		
		
		
		
		
		
		
		
		public IEnumerator<Pix> GetEnumerator()
		{
			return new PixArrayEnumerator(this);
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return new PixArrayEnumerator(this);
		}					
		
		protected override void Dispose(bool disposing)
		{
			IntPtr handle = _handle.Handle;
			LeptonicaApi.Native.pixaDestroy(ref handle);
			_handle = new HandleRef(this, handle);
		}
		
		#endregion
	}
}

