using System;
using System.Data;

using System.Data.SqlClient;
using System.Threading;
using edu.jhu.Cas.Util;

namespace edu.jhu.Cas.Util {
    /*
     * implementation of SqlDataReader that ensures unique, non-null/empty column names
     * 
     * also ensure DataReader disposes of QUICKLY by cancelling the command it came from, BEFORE trying to dispose of the reader.
     * it is very important to do this.  
     * to verify, try disposing of a reader whose command told it to 'select * from photoobj' BEFORE cancelling the command
     * 
     * Why is it in this class?
     * SqlDataReader is generated by SqlCommand.ExecuteReader.
     * So it is usually disposed of BEFORE SqlCommand.
     * But SqlCommand.Cancel needs to be called BEFORE SqlDataReader is disposed.
     * Yay cyclic dependency.  Fight fire with fire.
     */
    public class ReaderRapper : IDataReader {
        private SqlDataReader Reader;
        private string[] cnames;
        private const string DEF_NAME = "Column";
        private int ccnt = 1;
        SqlCommand parentCom;
        
        public ReaderRapper(SqlCommand parentCom) {
            this.parentCom = parentCom;
            Reader = parentCom.ExecuteReader();
            cnames = new string[Reader.FieldCount];
            string cname = "";
            for (int i = 0; i < cnames.Length; i++) {
                cname = Reader.GetName(i);
                if (cname == null || cname.Trim() == "" || HasColumnName(cname)) {
                    do {
                        cname = DEF_NAME + (ccnt++);
                    } while (HasColumnName(cname));
                }
                cnames[i] = cname;
            }
        }
        public string GetName(int i) {
            return cnames[i];
        }
        private bool HasColumnName(string name){
            string cname = "";
            name = name.ToLower().Trim();
            for (int i = 0; i < cnames.Length; i++) {
                if (cnames[i] == null) continue;
                cname = cnames[i].ToLower().Trim();
                if (cname == name) return true;
            }
            return false;
        }
        public void Close() { Reader.Close(); }
        public DataTable GetSchemaTable() { return Reader.GetSchemaTable(); }
        public bool NextResult() { return Reader.NextResult(); }
        public bool Read() { return Reader.Read(); }
        public bool IsClosed {
            get {
                return Reader.IsClosed;
            }
        }
        public void Dispose() {
            try { parentCom.Cancel(); } catch { }
            Reader.Dispose();
        }
        public int Depth {
            get {
                return Reader.Depth;
            }
        }
        public int RecordsAffected {
            get {
                return Reader.RecordsAffected;
            }
        }
        public string GetDataTypeName(int i) {
            return Reader.GetDataTypeName(i);
        }
        public Type GetFieldType(int i) {
            return Reader.GetFieldType(i);
        }
        public object GetValue(int i) {
            return Reader.GetValue(i);
        }
        public int GetValues(object[] o) {
            return Reader.GetValues(o);
        }
        public int GetOrdinal(string s) {
            return Reader.GetOrdinal(s);
        }
        public bool GetBoolean(int i) {
            return Reader.GetBoolean(i);
        }
        public byte GetByte(int i) {
            return Reader.GetByte(i);
        }
        public long GetBytes(int i, long l, byte[] ba, int a, int b) {
            return Reader.GetBytes(i,l,ba,a,b);
        }
        public char GetChar(int i) {
            return Reader.GetChar(i);
        }
        public long GetChars(int a, long b, char[] c, int d, int e) {
            return Reader.GetChars(a, b, c, d, e);
        }
        public Guid GetGuid(int i) {
            return Reader.GetGuid(i);
        }
        public short GetInt16(int i) {
            return Reader.GetInt16(i);
        }
        public int GetInt32(int i) {
            return Reader.GetInt32(i);
        }
        public long GetInt64(int i) {
            return Reader.GetInt64(i);
        }
        public float GetFloat(int i) {
            return Reader.GetFloat(i);
        }
        public Double GetDouble(int i) {
            return Reader.GetDouble(i);
        }
        public string GetString(int i) {
            return Reader.GetString(i);
        }
        public decimal GetDecimal(int i) {
            return Reader.GetDecimal(i);
        }
        public DateTime GetDateTime(int i) {
            return Reader.GetDateTime(i);
        }
        public bool IsDBNull(int i) {
            return Reader.IsDBNull(i);
        }
        public int FieldCount {
            get {
                return Reader.FieldCount;
            }
        }
        public object this[int i] {
            get {
                return Reader[i];
            }
        }
        public object this[string i] {
            get {
                return Reader[i];
            }
        }
        public IDataReader GetData(int i) {
            throw new NotImplementedException();
            return Reader.GetData(i);
        }
    }

}
