/*--------------------------------------------------------------------------
         *                Main
         *--------------------------------------------------------------------------*/
        static void Main(string[] args)
        {
            //  1.  Create a DynamoDB client connected to a DynamoDB-Local instance
            Console.WriteLine(stepString, 1,
                              "Create a DynamoDB client connected to a DynamoDB-Local instance");
            if (!CreateClient(true) || !Pause())
            {
                return;
            }

            //  1a.  Create a CosmosDB client connected to a DynamoDB-Local instance
            if (!CreateClient_CosmosDB(true) || !Pause())
            {
                return;
            }

            //  2.  Create a DynamoDB table for movie data asynchronously
            Console.WriteLine(stepString, 2,
                              "Create a table for movie data");
            CreatingTable_async(movies_table_name,
                                movie_items_attributes,
                                movies_key_schema,
                                movies_table_provisioned_throughput).Wait();
            if (!Pause() || operationFailed)
            {
                return;
            }

            try { moviesTable = Table.LoadTable(CosmosDBvDynamoDB.client, movies_table_name); }
            catch (Exception ex)
            {
                operationFailed = true;
                Console.WriteLine(
                    " Error: Could not access the new '{0}' table after creating it;\n" +
                    "        Reason: {1}.", movies_table_name, ex.Message);
                Pause();
                return;
            }

            //  2a.  Create a Cosmosdb database and collection for movie data asynchronously
            CreatingTable_async_CosmosDB(
                movies_table_name,
                partition_key_name,
                movie_collection_provisioned_throughput).Wait();
            if (!Pause() || operationFailed)
            {
                return;
            }


            //  3.  Load movie data into the Movies table asynchronously into DynamoDB
            if ((moviesTableDescription != null) &&
                (moviesTableDescription.ItemCount == 0))
            {
                Console.WriteLine(stepString, 3,
                                  "Load movie data into the Movies table");
                LoadingData_async(moviesTable, movieDataPath).Wait();
                if (!Pause() || operationFailed)
                {
                    return;
                }

                //  3a.  Load movie data into the Movies table asynchronously into CosmosDB
                LoadingData_async_CosmosDB(movieDataPath).Wait();
                if (!Pause() || operationFailed)
                {
                    return;
                }
            }
            else
            {
                Console.WriteLine(stepString, 3,
                                  "Skipped: Movie data is already loaded in the Movies table");
                if (!Pause())
                {
                    return;
                }
            }



            Console.WriteLine(stepString, 4,
                              "Add a new movie to the Movies table");
            //  4.  Add a new movie to the Movies table - DynamoDB
            Amazon.DynamoDBv2.DocumentModel.Document newItemDocument = new Amazon.DynamoDBv2.DocumentModel.Document
            {
                ["year"]  = 2018,
                ["title"] = "The Big New Movie",
                ["info"]  = Amazon.DynamoDBv2.DocumentModel.Document.FromJson(
                    "{\"plot\" : \"Nothing happens at all.\",\"rating\" : 0}")
            };
            WritingNewMovie_async(newItemDocument).Wait();
            if (!Pause() || operationFailed)
            {
                return;
            }

            //  4a.  Add a new movie to the Movies table - CosmosDB
            MovieModel mv = new MovieModel()
            {
                Id        = Guid.NewGuid().ToString(),
                Title     = "The Big New Movie",
                Year      = 2018,
                MovieInfo = new MovieInfo()
                {
                    Plot = "Nothing happens at all.", Rating = 0
                }
            };

            WritingNewMovie_async_cosmosDB(mv).Wait();
            if (!Pause() || operationFailed)
            {
                return;
            }


            Console.WriteLine(stepString, 5,
                              "Read and display the new movie record that was just added");
            //  5.  Read and display the new movie record that was just added - DynamoDB
            ReadingMovie_async(2018, "The Big New Movie", true).Wait();
            if (!Pause() || operationFailed)
            {
                return;
            }

            //  5a.  Read and display the new movie record that was just added - CosmosDB
            ReadingMovie_async_CosmosDB(2018, "The Big New Movie", true);
            if (!Pause() || operationFailed)
            {
                return;
            }

            //  6.  Update the new movie record in various ways
            //-------------------------------------------------
            //  6a.  Create an UpdateItemRequest to:
            //       -- modify the plot and rating of the new movie, and
            //       -- add a list of actors to it
            Console.WriteLine(stepString, "6a",
                              "Change the plot and rating for the new movie and add a list of actors");

            //DynamoDB
            UpdateItemRequest updateRequest = new UpdateItemRequest()
            {
                TableName = movies_table_name,
                Key       = new Dictionary <string, AttributeValue>
                {
                    { partition_key_name, new AttributeValue {
                          N = "2018"
                      } },
                    { sort_key_name, new AttributeValue {
                          S = "The Big New Movie"
                      } }
                },
                ExpressionAttributeValues = new Dictionary <string, AttributeValue>
                {
                    { ":r", new AttributeValue {
                          N = "5.5"
                      } },
                    { ":p", new AttributeValue {
                          S = "Everything happens all at once!"
                      } },
                    { ":a", new AttributeValue {
                          L = new List <AttributeValue>
                          {
                              new AttributeValue {
                                  S = "Larry"
                              },
                              new AttributeValue {
                                  S = "Moe"
                              },
                              new AttributeValue {
                                  S = "Curly"
                              }
                          }
                      } }
                },
                UpdateExpression = "SET info.rating = :r, info.plot = :p, info.actors = :a",
                ReturnValues     = "NONE"
            };

            UpdatingMovie_async(updateRequest, true).Wait();
            if (!Pause() || operationFailed)
            {
                return;
            }

            //CosmosDB
            var       doc  = ReadingMovieItem_async_CosmosDB(2018, "The Big New Movie");
            MovieInfo info = new MovieInfo
            {
                Rating = 5.5,
                Plot   = "Everything happens all at once!",
                Actors = new string[] { "Larry", "Moe", "Curly" }
            };

            doc.MovieInfo = info;
            UpdatingMovie_async_CosmosDB(doc, true).Wait();
            if (!Pause() || operationFailed)
            {
                return;
            }

            //  6b  Change the UpdateItemRequest so as to increment the rating of the
            //      new movie, and then make the update request asynchronously.
            Console.WriteLine(stepString, "6b",
                              "Increment the new movie's rating atomically");

            Console.WriteLine("  -- Incrementing the rating of the new movie by 1...");

            //DynamoDB
            updateRequest.ExpressionAttributeValues = new Dictionary <string, AttributeValue>
            {
                { ":inc", new AttributeValue {
                      N = "1"
                  } }
            };
            updateRequest.UpdateExpression = "SET info.rating = info.rating + :inc";
            UpdatingMovie_async(updateRequest, true).Wait();
            if (!Pause() || operationFailed)
            {
                return;
            }

            //CosmosDB
            var docList = ReadingMovieItem_async_List_CosmosDB(2018, "The Big New Movie");

            foreach (var docObject in docList)
            {
                info = docObject.MovieInfo;
                if (info != null)
                {
                    info.Rating++;
                    UpdatingMovie_async_CosmosDB(doc, true).Wait();
                    if (!Pause() || operationFailed)
                    {
                        return;
                    }
                }
            }

            //  6c  Change the UpdateItemRequest so as to increment the rating of the
            //      new movie, and then make the update request asynchronously.
            Console.WriteLine(stepString, "6c",
                              "Now try the same increment again with a condition that fails... ");
            Console.WriteLine("  -- Now trying to increment the new movie's rating, but this time\n" +
                              "     ONLY ON THE CONDITION THAT the movie has more than 3 actors...");

            //DynamoDB
            updateRequest.ExpressionAttributeValues.Add(":n", new AttributeValue {
                N = "0"
            });

            updateRequest.ConditionExpression = "size(info.actors) > :n";
            UpdatingMovie_async(updateRequest, true).Wait();
            if (!Pause() || operationFailed)
            {
                return;
            }

            //CosmosDB
            FeedIterator <MovieModel> result = ReadingMovieItem_async_List_CosmosDB("select * from Movies c where c.year=2018 and c.title=\"The Big New Movie\" and ARRAY_LENGTH(c.info.actors)>1");
            List <Task> tasks = new List <Task>();

            while (result.HasMoreResults)
            {
                var resultModel = result.ReadNextAsync();
                resultModel.Wait();
                foreach (var movie in resultModel.Result.ToList <MovieModel>())
                {
                    movie.MovieInfo.Rating++;

                    tasks.Add(UpdatingMovie_async_CosmosDB(doc, true));
                    if (operationFailed)
                    {
                        return;
                    }
                }
            }
            Task.WhenAll(tasks).Wait();

            //  7.  Try conditionally deleting the movie that we added

            //  7a.  Try conditionally deleting the movie that we added
            Console.WriteLine(stepString, "7a",
                              "Try deleting the new movie record with a condition that fails");
            Console.WriteLine("  -- Trying to delete the new movie,\n" +
                              "     -- but ONLY ON THE CONDITION THAT its rating is 8.0 or less...");

            //DynamoDB
            Expression condition = new Expression();

            condition.ExpressionAttributeValues[":val"] = 8.0;
            condition.ExpressionStatement = "info.rating <= :val";
            DeletingItem_async(moviesTable, 2018, "The Big New Movie", condition).Wait();
            if (!Pause() || operationFailed)
            {
                return;
            }

            //ComosDB
            DeletingItem_async_CosmosDB("select c from c join d in c.info where d.rating<8 AND c.year=2018 AND c.title='The Big New Movie'").GetAwaiter();
            if (!Pause() || operationFailed)
            {
                return;
            }

            //  7b.  Now increase the cutoff to 7.0 and try to delete again...
            Console.WriteLine(stepString, "7b",
                              "Now increase the cutoff to 7.0 and try to delete the movie again...");
            Console.WriteLine("  -- Now trying to delete the new movie again,\n" +
                              "     -- but this time on the condition that its rating is 7.0 or less...");
            //DynamoDB
            condition.ExpressionAttributeValues[":val"] = 8.0;

            DeletingItem_async(moviesTable, 2018, "The Big New Movie", condition).Wait();
            if (!Pause()) //|| operationFailed)
            {
                return;
            }

            //CosmosDB
            DeletingItem_async_CosmosDB("select * from c where c.info.rating>7 AND c.year=2018 AND c.title='The Big New Movie'").GetAwaiter();

            //  8.  Query the Movies table in 3 different ways
            Search search;

            //  8a. Just query on the year
            Console.WriteLine(stepString, "8a",
                              "Query the Movies table using a Search object for all movies from 1985");
            Console.WriteLine("  -- First, create a Search object...");

            //DynamoDB
            try { search = moviesTable.Query(1985, new Expression()); }
            catch (Exception ex)
            {
                Console.WriteLine("     ERROR: Failed to create the Search object because:\n            " +
                                  ex.Message);
                Pause();
                return;
            }
            Console.WriteLine("     -- Successfully created the Search object,\n" +
                              "        so now we'll display the movies retrieved by the query:");
            if ((search == null) || !Pause())
            {
                return;
            }
            SearchListing_async(search).Wait();
            if (!Pause() || operationFailed)
            {
                return;
            }

            //CosmosDB
            result = ReadingMovieItem_async_List_CosmosDB("select * from c where c.year=1985");
            while (result.HasMoreResults)
            {
                var resultModel = result.ReadNextAsync();
                resultModel.Wait();
                foreach (var movie in resultModel.Result.ToList <MovieModel>())
                {
                    Console.WriteLine(movie.PrintInfo());
                    if (operationFailed)
                    {
                        return;
                    }
                }
            }



            //  8b. SearchListing_async
            Console.WriteLine(stepString, "8b",
                              "Query for 1992 movies with titles from B... to Hzz... using Table.Query");
            Console.WriteLine("  -- Now setting up a QueryOperationConfig for the 'Search'...");

            //DynamoDB
            QueryOperationConfig config = new QueryOperationConfig
            {
                Filter = new QueryFilter()
            };

            config.Filter.AddCondition("year", QueryOperator.Equal, new DynamoDBEntry[] { 1992 });
            config.Filter.AddCondition("title", QueryOperator.Between, new DynamoDBEntry[] { "B", "Hzz" });
            config.AttributesToGet = new List <string> {
                "year", "title", "info"
            };
            config.Select = SelectValues.SpecificAttributes;
            Console.WriteLine("     -- Creating the Search object based on the QueryOperationConfig");
            try { search = moviesTable.Query(config); }
            catch (Exception ex)
            {
                Console.WriteLine("     ERROR: Failed to create the Search object because:\n            " +
                                  ex.Message);
                if (!Pause() || operationFailed)
                {
                    return;
                }
            }
            Console.WriteLine("     -- Successfully created the Search object,\n" +
                              "        so now we'll display the movies retrieved by the query.");
            if ((search == null) || !Pause())
            {
                return;
            }

            SearchListing_async(search).Wait();
            if (!Pause() || operationFailed)
            {
                return;
            }

            //CosmosDB
            result = ReadingMovieItem_async_List_CosmosDB("select c.year, c.title, c.info from c where c.year=1998 AND (CONTAINS(c.title,'B') OR CONTAINS(c.title,'Hzz'))");
            while (result.HasMoreResults)
            {
                var resultModel = result.ReadNextAsync();
                resultModel.Wait();
                foreach (var movie in resultModel.Result.ToList <MovieModel>())
                {
                    Console.WriteLine(movie.PrintInfo());
                    if (operationFailed)
                    {
                        return;
                    }
                }
            }


            //  8c. Query using a QueryRequest
            Console.WriteLine(stepString, "8c",
                              "Query the Movies table for 1992 movies with titles from M... to Tzz...");
            Console.WriteLine("  -- Next use a low-level query to retrieve a selection of movie attributes");

            //DynamoDB
            QueryRequest qRequest = new QueryRequest
            {
                TableName = "Movies",
                ExpressionAttributeNames = new Dictionary <string, string>
                {
                    { "#yr", "year" }
                },
                ExpressionAttributeValues = new Dictionary <string, AttributeValue>
                {
                    { ":qYr", new AttributeValue {
                          N = "1992"
                      } },
                    { ":tSt", new AttributeValue {
                          S = "M"
                      } },
                    { ":tEn", new AttributeValue {
                          S = "Tzz"
                      } }
                },
                KeyConditionExpression = "#yr = :qYr and title between :tSt and :tEn",
                ProjectionExpression   = "#yr, title, info.actors[0], info.genres, info.running_time_secs"
            };

            Console.WriteLine("     -- Using a QueryRequest to get the lead actor and genres of\n" +
                              "        1992 movies with titles between 'M...' and 'Tzz...'.");
            ClientQuerying_async(qRequest).Wait();
            if (!Pause() || operationFailed)
            {
                return;
            }

            //CosmosDB
            result = ReadingMovieItem_async_List_CosmosDB("select c.year, c.title, c.info from c where c.year=1992 AND (CONTAINS(c.title,'M') OR CONTAINS(c.title,'Tzz'))");
            while (result.HasMoreResults)
            {
                var resultModel = result.ReadNextAsync().Result;
                foreach (var movie in resultModel.ToList <MovieModel>())
                {
                    Console.WriteLine(movie.PrintInfo());
                    if (operationFailed)
                    {
                        return;
                    }
                }
            }

            //  9.  Try scanning the movies table to retrieve movies from several decades
            //  9a. Use Table.Scan with a Search object and a ScanFilter to retrieve movies from the 1950s
            Console.WriteLine(stepString, "9a",
                              "Scan the Movies table to retrieve all movies from the 1950's");

            //DynamoDB
            ScanFilter filter = new ScanFilter();

            filter.AddCondition("year", ScanOperator.Between, new DynamoDBEntry[] { 1950, 1959 });
            ScanOperationConfig scanConfig = new ScanOperationConfig
            {
                Filter = filter
            };

            Console.WriteLine("     -- Creating a Search object based on a ScanFilter");
            try { search = moviesTable.Scan(scanConfig); }
            catch (Exception ex)
            {
                Console.WriteLine("     ERROR: Failed to create the Search object because:\n            " +
                                  ex.Message);
                Pause();
                return;
            }
            Console.WriteLine("     -- Successfully created the Search object");
            if ((search == null) || !Pause())
            {
                return;
            }

            SearchListing_async(search).Wait();
            if (!Pause() || operationFailed)
            {
                return;
            }

            //CosmosDB
            result = ReadingMovieItem_async_List_CosmosDB("select * from c where c.year BETWEEN 1950 AND 1959");
            while (result.HasMoreResults)
            {
                var resultModel = result.ReadNextAsync();
                resultModel.Wait();
                foreach (var movie in resultModel.Result.ToList <MovieModel>())
                {
                    Console.WriteLine(movie.PrintInfo());
                    if (operationFailed)
                    {
                        return;
                    }
                }
            }


            //  9b. Use AmazonDynamoDBClient.Scan to retrieve movies from the 1960s
            Console.WriteLine(stepString, "9b",
                              "Use a low-level scan to retrieve all movies from the 1960's");
            Console.WriteLine("     -- Using a ScanRequest to get movies from between 1960 and 1969");

            //DynamoDB
            ScanRequest sRequest = new ScanRequest
            {
                TableName = "Movies",
                ExpressionAttributeNames = new Dictionary <string, string>
                {
                    { "#yr", "year" }
                },
                ExpressionAttributeValues = new Dictionary <string, AttributeValue>
                {
                    { ":y_a", new AttributeValue {
                          N = "1960"
                      } },
                    { ":y_z", new AttributeValue {
                          N = "1969"
                      } },
                },
                FilterExpression     = "#yr between :y_a and :y_z",
                ProjectionExpression = "#yr, title, info.actors[0], info.directors, info.running_time_secs"
            };

            ClientScanning_async(sRequest).Wait();
            if (operationFailed)
            {
                return;
            }

            //CosmosDB
            result = ReadingMovieItem_async_List_CosmosDB("select c.title, c.year, c.info.actors[0], c.info.directors,c.info.running_time_secs from c where  c.year BETWEEN 1960 AND 1969");

            while (result.HasMoreResults)
            {
                var resultModel = result.ReadNextAsync();
                resultModel.Wait();
                foreach (var movie in resultModel.Result.ToList <MovieModel>())
                {
                    Console.WriteLine(movie.PrintInfo());
                    if (operationFailed)
                    {
                        return;
                    }
                }
            }

            //  10.  Finally, delete the Movies table and all its contents
            Console.WriteLine(stepString, 10,
                              "Finally, delete the Movies table and all its contents");

            //DynamoDB
            DeletingTable_async(movies_table_name).Wait();

            //CosmosDB
            DeletingCollection_async_CosmosDB().Wait();
            //For Cosmos DB Database needs to be delted for complete clean up
            DeletingDB_async_CosmosDB().Wait();
            // End:
            Console.WriteLine(
                "\n=================================================================================" +
                "\n            This concludes the DynamoDB viz-a-viz cosmodDB demo program" +
                "\n=================================================================================" +
                "\n                      ...Press any key to exit");
            Console.ReadKey();

            return;
        }