using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Amazon.SimpleDB;
using Amazon.SimpleDB.Model;
using Ionic.Utils.Zip;
using Attribute = Amazon.SimpleDB.Model.Attribute;
using CensusParser;


namespace CensusDB
{
    public class CensusSimpleDB
    {
        public CensusSimpleDB(AmazonSimpleDB serviceArg)
        {
            service = serviceArg; 
        }

        private AmazonSimpleDB service;

        public  void DeleteAllItems(string queryExpression, string domain)
        {
            // Go through all the items and delete all the attributes
            string nextToken = null;

            try
            {
                // Since you may have more than 100 SimpleDB 
                // Domains, we're going through loop through them
                do
                {
                    QueryRequest request = new QueryRequest();
                    request.DomainName = domain;
                    request.QueryExpression = queryExpression;

                    if (nextToken != null)
                        request.NextToken = nextToken;

                    QueryResponse response = service.Query(request);

                    if (response.IsSetQueryResult())
                    {
                        List<string> regionItemNames = response.QueryResult.ItemName;

                        nextToken = response.QueryResult.NextToken;

                        if (regionItemNames.Count == 0)
                        {
                            Console.WriteLine("There are no Census Regions in your SimpleDB Account.  Run option 2 to add data to your account.");
                        }
                        else
                        {
                            Console.Write("Deleting : " + queryExpression + " : " + regionItemNames.Count + "");
                            foreach (string rItemName in regionItemNames)
                            {
                                DeleteRegionFromItemName(rItemName, domain);
                            }
                        }
                    }
                    Console.WriteLine();

                } while (nextToken != null);

            }
            catch (AmazonSimpleDBException sdbException)
            {
                Console.WriteLine(" SimpleDB Exception : " + sdbException.Message);
            }
        }

        public void DeleteDomain(string domainName)
        {
            try
            {
                DeleteDomainRequest request = new DeleteDomainRequest();
                request.DomainName = domainName;

                DeleteDomainResponse response = service.DeleteDomain(request);
            }
            catch (AmazonSimpleDBException sdbException)
            {
                Console.WriteLine(" SimpleDB Exception : " + sdbException.Message);
            }
        }

        public void DeleteRegionFromItemName(string itemName, string domain)
        {
            try
            {

                DeleteAttributesRequest request = new DeleteAttributesRequest();
                request.DomainName = domain;
                request.ItemName = itemName;

                DeleteAttributesResponse response = service.DeleteAttributes(request);
            }
            catch (AmazonSimpleDBException sdbException)
            {
                Console.WriteLine(" SimpleDB Exception : " + sdbException.Message);
            }
        }

        public bool DoesSimpleDBDomainExist(string domain)
        {
            bool ret = false;

            string nextToken = null;

            try
            {

                // Since you may have more than 100 SimpleDB 
                // Domains, we're going through loop through them
                do
                {
                    ListDomainsRequest request = new ListDomainsRequest();

                    // If we're iterating through the next set of 
                    if (nextToken != null)
                    {
                        request.NextToken = nextToken;
                    }

                    ListDomainsResponse response = service.ListDomains(request);

                    nextToken = response.ListDomainsResult.NextToken;

                    List<string> domainNameList = response.ListDomainsResult.DomainName;

                    // Does the returned list of Domains contain the Domain we're looking for?
                    if (domainNameList.Contains(domain))
                    {
                        ret = true;
                    }

                } while ((nextToken != null) && (ret == false));

            }
            catch (AmazonSimpleDBException sdbException)
            {
                Console.WriteLine(" SimpleDB Exception : " + sdbException.Message);
            }

            return ret;
        }

        public void AddDemoRegionDetailsToSimpleDB(State region, string domain)
        {

            // Go through all of the subregions in the region and add a row into
            // SimpleDB for that region (row with a RegionDetail attribute)
            try
            {

                foreach (Region r in region.RegionsInState)
                {
                    string regionName = region.RegionsInState[0].Name;


                    PutAttributesRequest request = new PutAttributesRequest();
                    request.DomainName = domain;
                    List<ReplaceableAttribute> attribs = new List<ReplaceableAttribute>();

                    Console.Write("Adding : Region : " + regionName + " Sub : " + r.Name);

                    attribs.Add(new ReplaceableAttribute { Name = "RegionName", Value = regionName, Replace = false });


                    attribs.Add(new ReplaceableAttribute { Name = "SubRegion", Value = r.Name, Replace = false });
                    attribs.Add(new ReplaceableAttribute { Name = "TotalPopulation", Value = Convert.ToString(r.TotalPopulation), Replace = false });
                    attribs.Add(new ReplaceableAttribute { Name = "FemalePopulation", Value = Convert.ToString(r.FemalePopulation), Replace = false });
                    attribs.Add(new ReplaceableAttribute { Name = "MalePopulation", Value = Convert.ToString(r.MalePopulation), Replace = false });

                    foreach (PopulationAgeRange p in r.PopRanges)
                    {
                        attribs.Add(new ReplaceableAttribute { Name = Convert.ToString(p.StartRange) + " to " + Convert.ToString(p.EndRange), Value = Convert.ToString(p.Population), Replace = false });
                        Console.Write(".");
                    }

                    request.Attribute = attribs;
                    request.ItemName = Guid.NewGuid().ToString();

                    PutAttributesResponse response = service.PutAttributes(request);
                    Console.WriteLine(" -- Complete");
                }

            }
            catch (AmazonSimpleDBException sdbException)
            {
                Console.WriteLine(" SimpleDB Exception : " + sdbException.Message);
            }
        }

        public void CreateSimpleDBDomain(string domain)
        {
            try
            {

                CreateDomainRequest request = new CreateDomainRequest();
                request.DomainName = domain;

                CreateDomainResponse response = service.CreateDomain(request);
            }
            catch (AmazonSimpleDBException sdbException)
            {
                Console.WriteLine(" SimpleDB Exception : " + sdbException.Message);
            }
        }

        public Dictionary<string, string> GetRegionAttribs(string itemName, string domain)
        {
            Dictionary<string, string> regionDictionary = new Dictionary<string, string>();

            try
            {
                GetAttributesRequest request = new GetAttributesRequest();
                request.DomainName = domain;
                request.ItemName = itemName;

                GetAttributesResponse response = service.GetAttributes(request);

                if (response.IsSetGetAttributesResult())
                {
                    List<Attribute> attrList = response.GetAttributesResult.Attribute;
                    foreach (Attribute attr in attrList)
                    {
                        regionDictionary.Add(attr.Name, attr.Value);
                    }
                }

            }
            catch (AmazonSimpleDBException sdbException)
            {
                Console.WriteLine(" SimpleDB Exception : " + sdbException.Message);
            }

            return regionDictionary;
        }

        public string GetRegionNameFromItemName(string rItemName, string domain)
        {
            string result = "<invalid>";

            try
            {
                GetAttributesRequest request = new GetAttributesRequest();
                request.DomainName = domain;
                request.ItemName = rItemName;

                GetAttributesResponse response = service.GetAttributes(request);

                if (response.IsSetGetAttributesResult())
                {
                    List<Attribute> attrList = response.GetAttributesResult.Attribute;
                    foreach (Attribute attr in attrList)
                    {
                        if (attr.Name == "Region")
                        {
                            result = attr.Value;
                            break;
                        }
                    }
                }

            }
            catch (AmazonSimpleDBException sdbException)
            {
                Console.WriteLine(" SimpleDB Exception : " + sdbException.Message);
            }

            return result;
        }


        public void AddDemoRegionToSimpleDB(string region, string domain)
        {

            try
            {
                PutAttributesRequest request = new PutAttributesRequest();
                request.DomainName = domain;
                List<ReplaceableAttribute> attribs = new List<ReplaceableAttribute>();

                attribs.Add(new ReplaceableAttribute
                {
                    Name = "Region",
                    Value = region,
                    Replace = false
                });

                request.Attribute = attribs;
                request.ItemName = Guid.NewGuid().ToString();

                PutAttributesResponse response = service.PutAttributes(request);
            }
            catch (AmazonSimpleDBException sdbException)
            {
                Console.WriteLine(" SimpleDB Exception : " + sdbException.Message);
            }
        }


        public void CleanCensusSimpleDB(string domain)
        {
            try
            {

                if (DoesSimpleDBDomainExist(domain))
                {
                    // Delete all the Region Items
                    DeleteAllItems("['Region' != '']", domain);

                    // Delete all the RegionName Items
                    DeleteAllItems("['RegionName' != '']", domain);

                    // Delete the SimpleDB Domain
                    DeleteDomain(domain);
                }
                else
                {
                    Console.WriteLine("SimpleDB Domain '" + domain + "' does not exist.");
                }
            }
            catch (AmazonSimpleDBException sdbException)
            {
                Console.WriteLine(" SimpleDB Exception : " + sdbException.Message);
            }
        }

        /// <summary>
        /// Gets the list of Census Regions inside SimpleDB.  
        /// 
        /// Assumes the domain (parameter) exists.  Make sure to check using the 
        /// DoesSimpleDBDomainExist method.
        /// </summary>
        /// <param name="domain"></param>
        /// <returns></returns>
        public List<string> GetCensusRegions(string domain)
        {
            List<string> regions = new List<string>();

            string nextToken = null;

            do
            {
                QueryRequest request = new QueryRequest();
                request.DomainName = domain;
                request.QueryExpression = "['Region' != '']";

                if (nextToken != null)
                    request.NextToken = nextToken;

                QueryResponse response = service.Query(request);

                if (response.IsSetQueryResult())
                {
                    List<string> regionItemNames = response.QueryResult.ItemName;

                    nextToken = response.QueryResult.NextToken;

                    if (regionItemNames.Count == 0)
                    {
                        Console.WriteLine("There are no Census Regions in your SimpleDB Account.  Run option 2 to add data to your account.");
                    }
                    else
                    {
                        foreach (string rItemName in regionItemNames)
                        {
                            string reg = GetRegionNameFromItemName(rItemName, domain);
                            regions.Add(reg);
                        }
                    }

                }
            } while (nextToken != null);


            return regions;
        }

        public State GetCensusRegionFromSimpleDB(string region, string domain)
        {
            State returnRegion = new State();

            List<Region> regionsInState = new List<Region>();

            string nextToken = null;
            try
            {
                do
                {
                    QueryRequest request = new QueryRequest();
                    request.DomainName = domain;
                    request.QueryExpression = "['RegionName' = '"+region+"']";

                    if (nextToken != null)
                        request.NextToken = nextToken;

                    QueryResponse response = service.Query(request);

                    List<string> regionItemNames = response.QueryResult.ItemName;

                    nextToken = response.QueryResult.NextToken;

                    if (regionItemNames.Count == 0)
                    {
                        Console.WriteLine("There are no matches for Region: "+region);
                    }
                    else
                    {
                

                        foreach(string itemName in regionItemNames)
                        {
                            // Pull in all the attributes from SimpleDB
                            Dictionary<string, string> regions = GetRegionAttribs(itemName, domain);

                            Region currRegion = new Region();
                            currRegion.Name = regions["SubRegion"];
                            currRegion.FemalePopulation = Convert.ToInt32(regions["FemalePopulation"]);
                            currRegion.MalePopulation = Convert.ToInt32(regions["MalePopulation"]);
                            currRegion.TotalPopulation = Convert.ToInt32(regions["TotalPopulation"]);

                            List<PopulationAgeRange> popRanges = new List<PopulationAgeRange>();
                
                            popRanges.Add(PullPopAgeRange(0, 4, regions));
                            popRanges.Add(PullPopAgeRange(5, 9, regions));
                            popRanges.Add(PullPopAgeRange(10, 14, regions));
                            popRanges.Add(PullPopAgeRange(15, 19, regions));
                            popRanges.Add(PullPopAgeRange(20, 24, regions));
                            popRanges.Add(PullPopAgeRange(25, 34, regions));
                            popRanges.Add(PullPopAgeRange(35, 44, regions));
                            popRanges.Add(PullPopAgeRange(45, 54, regions));
                            popRanges.Add(PullPopAgeRange(55, 59, regions));
                            popRanges.Add(PullPopAgeRange(60, 64, regions));
                            popRanges.Add(PullPopAgeRange(65, 74, regions));
                            popRanges.Add(PullPopAgeRange(75, 84, regions));
                            popRanges.Add(PullPopAgeRange(85, 200, regions));


                            currRegion.PopRanges = popRanges;

                            regionsInState.Add(currRegion);
                        }
                    }
                } while (nextToken != null);

                returnRegion.RegionsInState = regionsInState;
            }
            catch (AmazonSimpleDBException sdbException)
            {
                Console.WriteLine(" SimpleDB Exception : " + sdbException.Message);
            }
            return returnRegion;
        }

        public PopulationAgeRange PullPopAgeRange(int start, int end, Dictionary<string, string> regions)
        {
            PopulationAgeRange p = new PopulationAgeRange();

            p.StartRange = start;
            p.EndRange = end;
            try
            {
                string index = Convert.ToString(start) + " to " + Convert.ToString(end);
                p.Population = Convert.ToInt32(regions[index]);
            }
            catch (Exception e)
            {
                System.Diagnostics.Debug.WriteLine("Exception handled and swallowed. Message : "+e.Message);
                p.Population = 0;
            }

            return p;
            
        }
    }
}
