﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;

namespace EpubReader.Backend {
  public class Epub {
    private static string containerDocumentLocation = "META-INF/container.xml";
    private List<EpubContent> rootFiles;
    private byte[] fileBytes;
    private ZipArchive zip;

    public List<Rendition> Renditions;

    private Epub() {
    }

    public Epub(string path) {
      this.fileBytes = File.ReadAllBytes(path);
      var stream       = new MemoryStream(this.fileBytes);
      this.zip = new ZipArchive(stream);
      this.GetRootFilePaths();
      this.GetRenditions();
    }

    private void GetRootFilePaths() {
      string containerText = ReadTextFromZip(containerDocumentLocation);
      var containerXml     = new XmlDocument();
      containerXml.LoadXml(containerText);
      XmlNode rootFileNodes = containerXml["container"]["rootfiles"];

      this.rootFiles = new List<EpubContent>();
      foreach (XmlNode rootFile in rootFileNodes.ChildNodes) {
        string path = rootFile.Attributes["full-path"].Value;
        string type = rootFile.Attributes["media-type"].Value;
        this.rootFiles.Add(new EpubContent(path, type));
      }
    }

    private void GetRenditions() {
      this.Renditions = new List<Rendition>();
      foreach (var file in this.rootFiles) {
        string fileText = ReadTextFromZip(file.Path);
        var doc = new XmlDocument();
        doc.LoadXml(fileText);
        var rendition = new Rendition(file.Path, doc);
        this.Renditions.Add(rendition);
      }
    }

    private string ReadTextFromZip(string path) {
      var zipEntry = this.zip.Entries.FirstOrDefault(x => x.FullName == path);
      var stream   = zipEntry.Open();
      return ReadTextFromStream(stream);
    }

    private string ReadTextFromStream(Stream stream) {
      using (var reader = new StreamReader(stream)) {
        return reader.ReadToEnd();
      }
    }

    public string LoadRenditionPageItem(Rendition rendition, string pageID) {
      CheckIfRenditionPageIsInRendition(rendition, pageID);
      string path = BuildRenditionItemPath(rendition, pageID);
      return ReadTextFromZip(path);
    }

    private void CheckIfRenditionIsInEpub(Rendition rendition) {
      bool renditionExistsInEpub = this.Renditions.Where(r => r == rendition).Any();
      if (!renditionExistsInEpub) {
        throw new InvalidOperationException("Cannot load rendition page from an epub it is not a part of");
      }
    }

    private void CheckIfRenditionPageIsInRendition(Rendition rendition, string pageID) {
      CheckIfRenditionIsInEpub(rendition);
      bool pageExistsInRendition = rendition.PageIDs.Where(p => p == pageID).Any();
      if (!pageExistsInRendition) {
        //TODO: Do something here
      }
    }

    private string BuildRenditionItemPath(Rendition rendition, string pageID) {
      CheckIfRenditionPageIsInRendition(rendition, pageID);
      RenditionItem pageItem = rendition.Items.Where(p => p.ID == pageID).First();
      return string.Format("{0}{1}", rendition.RootPath, pageItem.Path);
    }
  }
}
