﻿/*
    This file is part of "Muffelsausen Code of Points".

    Copyright 2017 Jannis Mahlitz, Gero Treuner

    "Muffelsausen Code of Points" is free software: you can redistribute
    it and/or modify it under the terms of the GNU Affero General Public
    License as published by the Free Software Foundation, either version
    3 of the License, or (at your option) any later version.

    "Muffelsausen Code of Points" is distributed in the hope that it
    will be useful, but WITHOUT ANY WARRANTY; without even the implied
    warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    See the GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public
    License along with "Muffelsausen Code of Points".  If not, see
    <http://www.gnu.org/licenses/>.
*/

using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Driver;
using System;

namespace Muffelsausen.CodeOfPoints.DataAbstractionLayer
{
    public static class MongoDbContext
    {
        #region Members

        private static readonly Object lockObj = new Object();

        private static IDatabaseProvider databaseProvider;

        private static IMongoDatabase _database;

        #endregion

        #region Init

        /// <summary>
        /// Reads Data from the general configuration file and then creates an instance of the MongoDb. 
        /// These instance will then be used by every incomming request.
        /// ToDo: Testing with authentication
        /// </summary>
        /// <param name="mongoDbProvider">The database provider instance.</param>
        public static void Initialize(IDatabaseProvider mongoDbProvider)
        {
            if (mongoDbProvider == null)
            {
                throw new ArgumentNullException(nameof(mongoDbProvider));
            }

            if (databaseProvider == null)
            {
                lock (lockObj)
                {
                    if (databaseProvider == null)
                    {
                        databaseProvider = mongoDbProvider;

                        MongoDefaults.GuidRepresentation = GuidRepresentation.Standard;

                        _database = databaseProvider.GetDatabase();
                        if (_database == null)
                        {
                            throw new ArgumentException("Connection to Database did not work.");
                        }

                        RegisterDiscriminator<Competitor<decimal>>("Competitor`1");
                        RegisterDiscriminator<CompetitorPerson>("CompetitorPerson");
                        RegisterDiscriminator<TraRound>("TraRound");
                        RegisterDiscriminator<TraRoutine>("TraRoutine");
                        RegisterDiscriminator<PackBagRound>("PackBagRound");
                        RegisterDiscriminator<PackBagRoutine>("PackBagRoutine");
                    }
                }
            }
        }

        public static void CheckInitialized()
        {
            if (_database == null)
            {
                lock (lockObj)
                {
                    if (_database == null)
                    {
                        throw new InvalidOperationException("Initialize() must be called before using MongoDbContext.");
                    }
                }
            }
        }

        private static void RegisterDiscriminator<T>(string discriminator)
        {
            if (!BsonClassMap.IsClassMapRegistered(typeof(T)))
                BsonClassMap.RegisterClassMap<T>(cm =>
                {
                    cm.AutoMap();
                    cm.SetDiscriminator(discriminator);
                    cm.SetDiscriminatorIsRequired(true);
                });
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Returns the handle to a specific Collection from the Database. Documents are looked for depending on the class name.
        /// E.g: Competitor will be searched unter "Competitor"-Collections.
        /// </summary>
        /// <typeparam name="T">Is Type of the corresponding representation class (e.g. CompetitorPerson)</typeparam>
        /// <returns>Returns the requested Collection from Database</returns>
        public static IMongoCollection<T> Collection<T>()
        {
            CheckInitialized();
            return _database.GetCollection<T>(typeof(T).Name);
        }

        #endregion

    }


}
