//
// HostReadingDatabaseMapper.cs
//
// Author:
//       Carsten Sonne Larsen <cs@innolan.dk>
//
// Copyright (c) 2013 Carsten Sonne Larsen
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.Collections.Generic;
using MySql.Data.MySqlClient;
using Ntp.Analyzer.Log;
using Ntp.Analyzer.Objects;

namespace Ntp.Analyzer.Data.Static
{
    /// <summary>
    /// ORM mapper for table hostReading.
    /// </summary>
    public sealed class HostReadingDatabaseMapper : FilteredMapper<HostReading>
    {
        internal HostReadingDatabaseMapper (
            HostDatabaseMapper hostMapper,
            PeerDatabaseMapper peerMapper,
            LogBase log)
            : base(log)
        {
            this.hostMapper = hostMapper;
            this.peerMapper = peerMapper;
        }

        protected override bool UseCache {
            get { return false; }
        }

        private readonly HostDatabaseMapper hostMapper;
        private readonly PeerDatabaseMapper peerMapper;

        protected override string TableName {
            get { return "hostReading"; }
        }

        protected override string CreateSql {
            get {
                return
                    "CREATE TABLE hostReading ( " +
                    "    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, " +
                    "    time  TIMESTAMP NOT NULL, " +
                    "    hostId INT NOT NULL, " +
                    "    peerId INT NOT NULL, " +
                    "    offset DOUBLE NOT NULL, " +
                    "    jitter DOUBLE NOT NULL, " +
                    "    frequency DOUBLE NOT NULL, " +
                    "    stability DOUBLE NOT NULL, " +
                    "    FOREIGN KEY (hostId) REFERENCES host(id), " +
                    "    FOREIGN KEY (peerId) REFERENCES peer(id) " +
                    ") ENGINE=INNODB;";
            }
        }

        private const string selectSql =
            "SELECT " +
            " id, time, hostId, peerId, offset, jitter, frequency, stability" +
            " FROM hostReading ";
        private const string insertSql =
            "INSERT INTO hostReading (time, hostId, peerId, offset, jitter, frequency, stability) " +
            "VALUES ( @time, @hostId, @peerId, @offset, @jitter, @frequency, @stability);" +
            "SELECT LAST_INSERT_ID();";
        private const string updateSql =
            "UPDATE hostReading SET " +
            "name = @name, " +
            "hostId = @hostId, " +
            "peerId = @peerId, " +
            "offset = @offset, " +
            "jitter = @jitter, " +
            "frequency = @frequency, " +
            "stability = @stability " +
            "WHERE id = @id;";

        protected override void Insert (HostReading item)
        {
            lock (Locker) {
                try {
                    Open ();

                    Command.CommandText = insertSql;
                    Command.Prepare ();

                    Command.Parameters.AddWithValue ("@time", item.Time);
                    Command.Parameters.AddWithValue ("@hostId", item.Host.Id);
                    Command.Parameters.AddWithValue ("@peerId", item.Peer.Id);
                    Command.Parameters.AddWithValue ("@offset", item.Offset);
                    Command.Parameters.AddWithValue ("@jitter", item.Jitter);
                    Command.Parameters.AddWithValue ("@frequency", item.Frequency);
                    Command.Parameters.AddWithValue ("@stability", item.Stability);

                    object idObject = Command.ExecuteScalar ();
                    item.SetId (Convert.ToInt32 (idObject));
                } finally {
                    Close ();
                }
            }
        }

        protected override void Update (HostReading item)
        {
            lock (Locker) {
                try {
                    Open ();

                    Command.CommandText = updateSql;
                    Command.Prepare ();

                    Command.Parameters.AddWithValue ("@time", item.Time);
                    Command.Parameters.AddWithValue ("@hostId", item.Host.Id);
                    Command.Parameters.AddWithValue ("@peerId", item.Peer.Id);
                    Command.Parameters.AddWithValue ("@offset", item.Offset);
                    Command.Parameters.AddWithValue ("@jitter", item.Jitter);
                    Command.Parameters.AddWithValue ("@frequency", item.Frequency);
                    Command.Parameters.AddWithValue ("@stability", item.Stability);
                    Command.Parameters.AddWithValue ("@id", item.Id);
                    Command.ExecuteNonQuery ();
                } finally {
                    Close ();
                }
            }
        }

        /// <summary>
        /// Read all data from table in a sequential manner.
        /// </summary>
        /// <returns>The enumerator.</returns>
        public override IEnumerator<HostReading> GetEnumerator ()
        {
            lock (Locker) {

                string sql = selectSql;

                if (FilterTime != null)
                    sql += " WHERE time > '" + FilterTime.Value.ToString ("yyyy-MM-dd HH:mm") + "'";

                if (FilterHost != null)
                    sql += " AND hostId = " + FilterHost.Id;

                try {
                    Open ();
                    Command.CommandText = sql;
                    Reader = Command.ExecuteReader ();

                    while (Reader.Read ()) {
                        int id = Convert.ToInt32 (Reader ["id"]);
                        DateTime time = Convert.ToDateTime (Reader ["time"]);
                        int hostId = Convert.ToInt32 (Reader ["hostId"]);
                        Host host = hostMapper [hostId];
                        int peerId = Convert.ToInt32 (Reader ["peerId"]);
                        Peer peer = peerMapper [peerId];
                        double offset = Convert.ToDouble (Reader ["offset"]);
                        double jitter = Convert.ToDouble (Reader ["jitter"]);
                        double frequency = Convert.ToDouble (Reader ["frequency"]);
                        double stability = Convert.ToDouble (Reader ["stability"]);

                        HostReading reading = new HostReading (
                            id, time, host, peer, null,
                            offset, jitter, frequency, stability);

                        yield return reading;
                    }
                } finally {
                    Close ();
                }
            }
        }
    }
}
