using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;

namespace net.ivoa.data {
    /// <summary>
    /// IDataReader for CSV streams.
    /// Data types are determined by the first PRELOAD_ROWS.
    /// </summary>
    public class CSVDataReader : IDataReader {
        TextReader origReader;
        StreamReader tmpFileReader;
        Dictionary<string, int> col2Ordinal = new Dictionary<string, int>();
        public int rowsParsed;
        CSVStreamInfo csi;
        string tmpFileName;
        
        public CSVDataReader(TextReader s) {
            origReader = s;
        }
        public void UploadTo(string cstring, string tableName, bool tabExists) {
            Init(cstring, tableName, tabExists);
            using (SqlConnection con = new SqlConnection(cstring)) {
                con.Open();
                if (!tabExists) {//make the table if its new
                    using (SqlCommand com = con.CreateCommand()) {
                        com.CommandText = GetCreateTableQry(tableName);
                        com.ExecuteNonQuery();
                    }
                }
                using (SqlBulkCopy sbc = new SqlBulkCopy(con)) {
                    sbc.DestinationTableName = tableName;
                    sbc.BulkCopyTimeout = 28800;
                    sbc.WriteToServer(this);
                }
            }
        }
        // IDispose
        public void Dispose() {
            if(tmpFileReader != null)
                tmpFileReader.Close();
            if(tmpFileName != null)
                File.Delete(tmpFileName);
        }
        public int FieldCount { get { return csi.ColumnNames.Length; } }
        public object this[string colName] { get { return this[GetOrdinal(colName)]; } }
        public object this[int colIndex] { get { return csi.CurrentRow[colIndex]; } }
        public bool GetBoolean(int i) { return (bool)this[i]; }
        public byte GetByte(int i) { return (byte)this[i]; }
        public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferOffset, int length) { throw new NotImplementedException(); }
        public char GetChar(int i) { return (char)this[i]; }
        public long GetChars(int i, long fieldOffset, char[] buffer, int bufferOffset, int length) { throw new NotImplementedException(); }
        public IDataReader GetData(int i) { throw new NotImplementedException(); }
        public string GetDataTypeName(int i) { return (GetFieldType(i).Name); }
        public DateTime GetDateTime(int i) { return (DateTime)this[i]; }
        public decimal GetDecimal(int i) { return (decimal)this[i]; }
        public double GetDouble(int i) { return (double)this[i]; }
        public Type GetFieldType(int i) { throw new NotImplementedException("GetFieldType"); }
        public float GetFloat(int i) { return (float)this[i]; }
        public Guid GetGuid(int i) { return (Guid)this[i]; }
        public short GetInt16(int i) { return (short)this[i]; }
        public int GetInt32(int i) { return (int)this[i]; }
        public long GetInt64(int i) { return (long)this[i]; }
        public string GetName(int i) { return csi.ColumnNames[i]; }
        public int GetOrdinal(string colName) { return col2Ordinal[colName]; }
        public string GetString(int i) { return this[i].ToString(); }
        public object GetValue(int i) { return this[i]; }
        public int GetValues(object[] values) { throw new NotImplementedException(); }
        public bool IsDBNull(int i) { return false; }
        public int Depth {
            get { throw new NotImplementedException(); }
        }
        public bool IsClosed {
            get { return false; }
        }
        public int RecordsAffected {
            get { throw new NotImplementedException(); }
        }
        public void Close() {  }
        public DataTable GetSchemaTable() { throw new NotImplementedException(); }
        public bool NextResult() {
            throw new NotImplementedException("next result");
        }
        public bool Read() {
            string line = tmpFileReader.ReadLine();
            if (line == null) return false;
            else {
                csi.ParseRow(line);
                return true;
            }
        }
        public string GetCreateTableQry(string tableName) {
            string qry = "create table [" + tableName + "] (\n";
            for (int i = 0; i < csi.ColumnNames.Length; i++) {
                qry += "\n[" + csi.ColumnNames[i] + "]";
                qry += " " + csi.GetSqlType(i);
                if (csi.GetSqlType(i) == SqlDbType.VarChar)
                    qry += "(MAX)";
                qry += ",";
            }
            qry = qry.Trim(',');
            qry += ")";
            return qry;
        }
        void Init(string cstring, string tableName, bool tabExists) {
            tmpFileName = Path.GetTempFileName();
            //copying to a temp file
            using (StreamWriter sw = new StreamWriter(File.OpenWrite(tmpFileName))) {
                string line = origReader.ReadLine();
                while (line != null) {
                    sw.WriteLine(line);
                    line = origReader.ReadLine();
                }
                sw.Flush();
            }
            using (StreamReader sr = new StreamReader(File.OpenRead(tmpFileName))) {
                string line = sr.ReadLine();//header line
                line = line.Trim('#');//remove funky pound sign
                if (tabExists)
                    csi = new CSVStreamInfo(line, cstring, tableName);
                else {
                    csi = new CSVStreamInfo(line);
                    line = sr.ReadLine();//advance to first real row
                    while (line != null) {
                        csi.LearnColTypes(line);
                        line = sr.ReadLine();
                    }
                }
            }
            tmpFileReader = new StreamReader(File.OpenRead(tmpFileName));
            for (int i = 0; i < csi.ColumnNames.Length; i++)
                col2Ordinal.Add(csi.ColumnNames[i], i);
            tmpFileReader.ReadLine();//toss the header
        }
    }
}