/* 
 * You may amend and distribute as you like, but don't remove this header!
 * 
 * EPPlus provides server-side generation of Excel 2007 spreadsheets.
 * See http://www.codeplex.com/EPPlus for details.
 * 
 * All rights reserved.
 * 
 * EPPlus is an Open Source project provided under the 
 * GNU General Public License (GPL) as published by the 
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 * 
 * The GNU General Public License can be viewed at http://www.opensource.org/licenses/gpl-license.php
 * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html
 * 
 * The code for this project may be used and redistributed by any means PROVIDING it is 
 * not sold for profit without the author's written consent, and providing that this notice 
 * and the author's name and all copyright notices remain intact.
 * 
 * All code and executables are provided "as is" with no warranty either express or implied. 
 * The author accepts no liability for any damage or loss of business that this product may cause.
 *
 *  Code change notes:
 * 
 * Author							Change						Date
 * ******************************************************************************
 * Jan Källman                      Added       		        2012-05-01
 *******************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using OfficeOpenXml;
using System.Security.Cryptography.X509Certificates;
using System.Drawing;
using OfficeOpenXml.Style;
using OfficeOpenXml.Drawing.Chart;
namespace EPPlusSamples
{
    class Sample15
    {
        public static void VBASample(DirectoryInfo outputDir)
        {
            //Create a macro-enabled workbook from scratch.
            VBASample1(outputDir);
            
            //Open Sample 1 and add code to change the chart to a bubble chart.
            VBASample2(outputDir);

            //Simple battleships game from scratch.
            VBASample3(outputDir);
        }
        private static void VBASample1(DirectoryInfo outputDir)
        {
            ExcelPackage pck = new ExcelPackage();

            //Add a worksheet.
            var ws=pck.Workbook.Worksheets.Add("VBA Sample");
            ws.Drawings.AddShape("VBASampleRect", eShapeStyle.RoundRect);
            
            //Create a vba project             
            pck.Workbook.CreateVBAProject();

            //Now add some code to update the text of the shape...
            var sb = new StringBuilder();

            sb.AppendLine("Private Sub Workbook_Open()");
            sb.AppendLine("    [VBA Sample].Shapes(\"VBASampleRect\").TextEffect.Text = \"This text is set from VBA!\"");
            sb.AppendLine("End Sub");
            pck.Workbook.CodeModule.Code = sb.ToString();            

            //And Save as xlsm
            pck.SaveAs(new FileInfo(outputDir.FullName + @"\sample15-1.xlsm"));
        }
        private static void VBASample2(DirectoryInfo outputDir)
        {
            //Open Sample 1 again
            ExcelPackage pck = new ExcelPackage(new FileInfo(outputDir.FullName + @"\sample1.xlsx"));
            //Create a vba project             
            pck.Workbook.CreateVBAProject();

            //Now add some code that creates a bubble chart...
            var sb = new StringBuilder();

            sb.AppendLine("Public Sub CreateBubbleChart()");
            sb.AppendLine("Dim co As ChartObject");
            sb.AppendLine("Set co = Inventory.ChartObjects.Add(10, 100, 400, 200)");
            sb.AppendLine("co.Chart.SetSourceData Source:=Range(\"'Inventory'!$B$1:$E$5\")");
            sb.AppendLine("co.Chart.ChartType = xlBubble3DEffect         'Add a bubblechart");
            sb.AppendLine("End Sub");

            //Create a new module and set the code
            var module = pck.Workbook.VbaProject.Modules.AddModule("EPPlusGeneratedCode");
            module.Code = sb.ToString();

            //Call the newly created sub from the workbook open event
            pck.Workbook.CodeModule.Code = "Private Sub Workbook_Open()\r\nCreateBubbleChart\r\nEnd Sub";

            //Optionally, Sign the code with your company certificate.
            /*            
            X509Store store = new X509Store(StoreLocation.CurrentUser);
            store.Open(OpenFlags.ReadOnly);
            pck.Workbook.VbaProject.Signature.Certificate = store.Certificates[0];
            */

            //And Save as xlsm
            pck.SaveAs(new FileInfo(outputDir.FullName + @"\sample15-2.xlsm"));
        }
        private static void VBASample3(DirectoryInfo outputDir)
        {
            //Now, lets do something a little bit more fun.
            //We are going to create a simple battleships game from scratch.

            ExcelPackage pck = new ExcelPackage();

            //Add a worksheet.
            var ws = pck.Workbook.Worksheets.Add("Battleship");

            ws.View.ShowGridLines = false;
            ws.View.ShowHeaders = false;

            ws.DefaultColWidth = 3;
            ws.DefaultRowHeight = 15;

            int gridSize=10;

            //Create the boards
            var board1 = ws.Cells[2, 2, 2 + gridSize - 1, 2 + gridSize - 1];
            var board2 = ws.Cells[2, 4+gridSize-1, 2 + gridSize-1, 4 + (gridSize-1)*2];
            CreateBoard(board1);
            CreateBoard(board2);
            ws.Select("B2");
            ws.Protection.IsProtected = true;
            ws.Protection.AllowSelectLockedCells = true;

            //Create the VBA Project
            pck.Workbook.CreateVBAProject();
            //Password protect your code
            pck.Workbook.VbaProject.Protection.SetPassword("EPPlus");

            //Add all the code from the textfiles in the Vba-Code sub-folder.
            pck.Workbook.CodeModule.Code = File.ReadAllText("..\\..\\VBA-Code\\ThisWorkbook.txt");
            
            //Add the sheet code
            ws.CodeModule.Code = File.ReadAllText("..\\..\\VBA-Code\\BattleshipSheet.txt");
            var m1=pck.Workbook.VbaProject.Modules.AddModule("Code");
            string code = File.ReadAllText("..\\..\\VBA-Code\\CodeModule.txt");
            
            //Insert your ships on the right board. you can changes these, but don't cheat ;)
            var ships = new string[]{
                "N3:N7",
                "P2:S2",
                "V9:V11",
                "O10:Q10",
                "R11:S11"};
            
            //Note: For security reasons you should never mix external data and code(to avoid code injections!), especially not on a webserver. 
            //If you deside to do that anyway, be very careful with the validation of the data.
            //Be extra carefull if you sign the code.
            //Read more here http://en.wikipedia.org/wiki/Code_injection

            code = string.Format(code, ships[0],ships[1],ships[2],ships[3],ships[4], board1.Address, board2.Address);  //Ships are injected into the constants in the module
            m1.Code = code;

            //Ships are displayed with a black background
            string shipsaddress = string.Join(",", ships);
            ws.Cells[shipsaddress].Style.Fill.PatternType = OfficeOpenXml.Style.ExcelFillStyle.Solid;
            ws.Cells[shipsaddress].Style.Fill.BackgroundColor.SetColor(Color.Black);

            var m2 = pck.Workbook.VbaProject.Modules.AddModule("ComputerPlay");
            m2.Code = File.ReadAllText("..\\..\\VBA-Code\\ComputerPlayModule.txt");

            var c1 = pck.Workbook.VbaProject.Modules.AddClass("Ship",false);
            c1.Code = File.ReadAllText("..\\..\\VBA-Code\\ShipClass.txt");

            //Add the info text shape.
            var tb = ws.Drawings.AddShape("txtInfo", eShapeStyle.Rect);
            tb.SetPosition(1, 0, 27, 0);
            tb.Fill.Color = Color.LightSlateGray;
            var rt1 = tb.RichText.Add("Battleships");
            rt1.Bold = true;
            tb.RichText.Add("\r\nDouble-click on the left board to make your move. Find and sink all ships to win!");

            //Set the headers.
            ws.SetValue("B1", "Computer Grid");
            ws.SetValue("M1", "Your Grid");
            ws.Row(1).Style.Font.Size = 18;

            AddChart(ws.Cells["B13"], "chtHitPercent", "Player");
            AddChart(ws.Cells["M13"], "chtComputerHitPercent", "Computer");

            ws.Names.Add("LogStart", ws.Cells["B24"]);
            ws.Cells["B24:X224"].Style.Border.BorderAround(ExcelBorderStyle.Thin, Color.Black);
            ws.Cells["B25:X224"].Style.Font.Name = "Consolas";
            ws.SetValue("B24", "Log");
            ws.Cells["B24"].Style.Font.Bold = true;
            ws.Cells["B24:X24"].Style.Border.BorderAround(ExcelBorderStyle.Thin, Color.Black);
            var cf=ws.Cells["B25:B224"].ConditionalFormatting.AddContainsText();
            cf.Text = "hit";
            cf.Style.Font.Color.Color = Color.Red;

            //If you have a valid certificate for code signing you can use this code to set it.
            ///*** Try to find a cert valid for signing... ***/
            //X509Store store = new X509Store(StoreLocation.CurrentUser);
            //store.Open(OpenFlags.ReadOnly);   
            //foreach (var cert in store.Certificates)
            //{
            //    if (cert.HasPrivateKey && cert.NotBefore <= DateTime.Today && cert.NotAfter >= DateTime.Today)
            //    {
            //        pck.Workbook.VbaProject.Signature.Certificate = cert;
            //        break;
            //    }
            //}

            pck.SaveAs(new FileInfo(outputDir.FullName + @"\sample15-3.xlsm"));
        }

        private static void AddChart(ExcelRange rng,string name, string prefix)
        {
            var chrt = (ExcelPieChart)rng.Worksheet.Drawings.AddChart(name, eChartType.Pie);
            chrt.SetPosition(rng.Start.Row-1, 0, rng.Start.Column-1, 0);
            chrt.To.Row = rng.Start.Row+9;
            chrt.To.Column = rng.Start.Column + 9;
            chrt.Style = eChartStyle.Style18;
            chrt.DataLabel.ShowPercent = true;

            var serie = chrt.Series.Add(rng.Offset(2, 2, 1, 2), rng.Offset(1, 2, 1, 2));
            serie.Header = "Hits";
            
            chrt.Title.Text = "Hit ratio";
            
            var n1 = rng.Worksheet.Names.Add(prefix + "Misses", rng.Offset(2, 2));
            n1.Value = 0;
            var n2 = rng.Worksheet.Names.Add(prefix + "Hits", rng.Offset(2, 3));
            n2.Value = 0;
            rng.Offset(1, 2).Value = "Misses";
            rng.Offset(1, 3).Value = "Hits";            
        }

        private static void CreateBoard(ExcelRange rng)
        {
            //Create a gradiant background with one dark and one light blue color
            rng.Style.Fill.Gradient.Color1.SetColor(Color.FromArgb(0x80, 0x80, 0XFF));
            rng.Style.Fill.Gradient.Color2.SetColor(Color.FromArgb(0x20, 0x20, 0XFF));
            rng.Style.Fill.Gradient.Type = ExcelFillGradientType.None;
            for (int col = 0; col <= rng.End.Column - rng.Start.Column; col++)
            {
                for (int row = 0; row <= rng.End.Row - rng.Start.Row; row++)
                {
                    if (col % 4 == 0)
                    {
                        rng.Offset(row, col, 1, 1).Style.Fill.Gradient.Degree = 45;
                    }
                    if (col % 4 == 1)
                    {
                        rng.Offset(row, col, 1, 1).Style.Fill.Gradient.Degree = 70;
                    }
                    if (col % 4 == 2)
                    {
                        rng.Offset(row, col, 1, 1).Style.Fill.Gradient.Degree = 110;
                    }
                    else
                    {
                        rng.Offset(row, col, 1, 1).Style.Fill.Gradient.Degree = 135;
                    }
                }
            }
            //Set the inner cell border to thin, light gray
            rng.Style.Border.Top.Style = ExcelBorderStyle.Thin;
            rng.Style.Border.Top.Color.SetColor(Color.Gray);
            rng.Style.Border.Right.Style = ExcelBorderStyle.Thin;
            rng.Style.Border.Right.Color.SetColor(Color.Gray);
            rng.Style.Border.Left.Style = ExcelBorderStyle.Thin;
            rng.Style.Border.Left.Color.SetColor(Color.Gray);
            rng.Style.Border.Bottom.Style = ExcelBorderStyle.Thin;
            rng.Style.Border.Bottom.Color.SetColor(Color.Gray);

            //Solid black border around the board.
            rng.Style.Border.BorderAround(ExcelBorderStyle.Medium, Color.Black);
        }
    }
}
