using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CBR.Core.Models;
using CBR.Core.Helpers;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Windows;
using System.Windows.Threading;
using System.Threading;
using System.Windows.Media.Imaging;
using SevenZip;
using CBR.Core.Files;
using System.Text.RegularExpressions;
namespace CBR.Core.Services
{
public class BookService : BookServiceBase
{
override internal void LoadCoverThread(object param)
{
SevenZipExtractor temp = null;
Book bk = param as Book;
try
{
temp = ZipHelper.Instance.GetExtractor(bk.FilePath);
bk.Size = temp.PackedSize / 1024 / 1024;
bk.PageCount = temp.ArchiveFileNames.Count;
foreach (ArchiveFileInfo fil in temp.ArchiveFileData)
{
if (!fil.IsDirectory && FileService.Instance.ImageExtension.Contains(Path.GetExtension(fil.FileName).ToUpper()))
{
using (MemoryStream stream = new MemoryStream())
{
temp.ExtractFile(fil.FileName, stream);
using (MemoryStream stream2 = new MemoryStream())
{
stream.WriteTo(stream2);
stream.Flush();
stream.Close();
stream2.Position = 0;
Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate
{
BitmapImage myImage = new BitmapImage();
myImage.BeginInit();
myImage.StreamSource = stream2;
myImage.CacheOption = BitmapCacheOption.OnLoad;
myImage.DecodePixelWidth = 70;
myImage.EndInit();
bk.Cover = myImage;
});
stream2.Flush();
stream2.Close();
}
}
return;
}
}
}
catch (Exception err)
{
ExceptionHelper.Manage("BookService:LoadCoverThread", err);
}
finally
{
ZipHelper.Instance.ReleaseExtractor(temp);
}
}
private int CheckFileNames(string fileName)
{
Regex MyRegex = new Regex("\\d+",
RegexOptions.IgnoreCase
| RegexOptions.Multiline
| RegexOptions.CultureInvariant
| RegexOptions.IgnorePatternWhitespace
| RegexOptions.Compiled
);
FileInfo fi = new FileInfo(fileName);
MatchCollection ms = MyRegex.Matches(fi.Name);
Match m = ms[ms.Count - 1];
return Convert.ToInt32(m.Value);
}
override public object LoadBook(Book bk)
{
SevenZipExtractor temp = null;
try
{
base.LoadBook(bk);
temp = ZipHelper.Instance.GetExtractor(bk.FilePath);
bk.Size = temp.PackedSize / 1024 / 1024;
bk.PageCount = temp.ArchiveFileNames.Count;
foreach (ArchiveFileInfo fil in temp.ArchiveFileData)
{
if (!fil.IsDirectory)
{
if (FileService.Instance.ImageExtension.Contains(Path.GetExtension(fil.FileName).ToUpper()))
{
Page item = new Page(bk, fil.FileName, bk.Pages.Count);
item.Index = CheckFileNames(fil.FileName);
bk.Pages.Add(item);
}
}
}
bk.Pages = bk.Pages.OrderBy(p => p.Index).ToList<Page>();
foreach (ArchiveFileInfo fil in temp.ArchiveFileData)
{
if (!fil.IsDirectory && fil.FileName.Contains(".dynamics.xml"))
{
Page pg = bk.Pages.Find(p => p.FileName == fil.FileName.Replace(".dynamics.xml", ""));
if (pg != null)
{
MemoryStream stream = new MemoryStream();
temp.ExtractFile(fil.FileName, stream);
MemoryStream stream2 = new MemoryStream();
stream.WriteTo(stream2);
stream.Flush();
stream.Close();
stream2.Position = 0;
pg.Frames = (List<Zone>)XmlHelper.Deserialize(stream2, typeof(List<Zone>));
stream2.Close();
if (pg.Frames == null) pg.Frames = new List<Zone>();
}
}
}
}
catch (Exception err)
{
ExceptionHelper.Manage("BookService:LoadBook", err);
}
finally
{
ZipHelper.Instance.ReleaseExtractor(temp);
}
return bk.Pages[0];
}
#region -----------------BOOK MANAGEMENT-----------------
override public void EditBook(Book bk)
{
SevenZipExtractor temp = null;
try
{
temp = ZipHelper.Instance.GetExtractor(bk.FilePath);
foreach (ArchiveFileInfo fil in temp.ArchiveFileData)
{
if (!fil.IsDirectory)
{
if (fil.FileName.Contains(".dynamics.xml"))
{
Page item = new Page(bk, fil.FileName, bk.Pages.Count);
bk.Pages.Add(item);
}
}
}
}
catch (Exception err)
{
ExceptionHelper.Manage("BookService:EditBook", err);
}
finally
{
ZipHelper.Instance.ReleaseExtractor(temp);
}
}
override public Book SaveBook(Book bk)
{
try
{
//first time we save as dynamic book
if (IsDynamic(bk) && Path.GetExtension(bk.FilePath) != ".dcb")
{
string newFile = FirstSaveDynamicBook(bk);
UnloadBook(bk);
return CreateBook(newFile);
}
else //only update the frame files
{
UpdateDynamicBook(bk);
}
}
catch (Exception err)
{
ExceptionHelper.Manage("BookService:SaveBook", err);
}
return null;
}
internal string FirstSaveDynamicBook(Book bk)
{
try
{
// create a temp folder
string tempFolder = Path.Combine(DirectoryHelper.ApplicationPath, Path.GetFileNameWithoutExtension(bk.FilePath));
DirectoryHelper.Check(tempFolder);
//extract the book content
ExtractBook(bk, tempFolder);
//serialize all frames even empty to create the files in the zip
foreach (Page pg in bk.Pages)
{
XmlHelper.Serialize(Path.Combine(tempFolder, pg.FileName + ".dynamics.xml"), pg.Frames);
}
// create a new file by compressing all temp folder content
string newComic = bk.FilePath.Replace(Path.GetExtension(bk.FilePath), ".dcb");
SevenZip.SevenZipCompressor cp = new SevenZip.SevenZipCompressor();
cp.ArchiveFormat = SevenZip.OutArchiveFormat.Zip;
string[] outputFiles = new DirectoryInfo(tempFolder).GetFiles("*.*").Select(p => p.FullName).ToArray();
using (FileStream fs = new FileStream(newComic, FileMode.Create))
{
cp.CompressFiles(fs, outputFiles);
}
//delete the temp folder
Directory.Delete(tempFolder, true);
return newComic;
}
catch (Exception err)
{
ExceptionHelper.Manage("BookService:FirstSaveDynamicBook", err);
}
return null;
}
internal void UpdateDynamicBook(Book bk)
{
try
{
}
catch (Exception err)
{
ExceptionHelper.Manage("BookService:UpdateDynamicBook", err);
}
}
internal Dictionary<int, string> GetBookContentList(Book bk)
{
SevenZipExtractor temp = null;
Dictionary<int, string> content = new Dictionary<int, string>();
try
{
temp = ZipHelper.Instance.GetExtractor(bk.FilePath);
foreach (ArchiveFileInfo fil in temp.ArchiveFileData)
{
if (!fil.IsDirectory)
{
content.Add(content.Count, fil.FileName);
}
}
}
catch (Exception err)
{
ExceptionHelper.Manage("BookService:GetBookContentList", err);
}
finally
{
ZipHelper.Instance.ReleaseExtractor(temp);
}
return content;
}
internal void ExtractBook(Book bk, string outputFolder)
{
SevenZipExtractor temp = null;
try
{
temp = ZipHelper.Instance.GetExtractor(bk.FilePath);
temp.PreserveDirectoryStructure = false;
temp.ExtractArchive(outputFolder);
}
catch (Exception err)
{
ExceptionHelper.Manage("BookService:ExtractBook", err);
}
finally
{
ZipHelper.Instance.ReleaseExtractor(temp);
}
}
#endregion
public BitmapImage GetImageFromStream(string zipFilePath, string fileName)
{
SevenZipExtractor temp = null;
BitmapImage result = null;
try
{
temp = ZipHelper.Instance.GetExtractor(zipFilePath);
MemoryStream stream = new MemoryStream();
temp.ExtractFile(fileName, stream);
result = StreamToImage.GetImageFromStreamBug(stream, 0);
}
catch (Exception err)
{
ExceptionHelper.Manage("BookService:GetImageFromStream", err);
}
finally
{
ZipHelper.Instance.ReleaseExtractor(temp);
}
return result;
}
#region -----------------BOOKMARK-----------------
override public void SetMark(Book bk, Page pg)
{
if (bk != null && pg != null)
bk.Bookmark = pg.FilePath;
}
override public Page GotoMark(Book bk)
{
if (bk != null)
{
if (!string.IsNullOrEmpty(bk.Bookmark))
{
foreach (Page pg in bk.Pages)
{
if (pg.FilePath == bk.Bookmark)
{
return pg;
}
}
}
}
return null;
}
#endregion
#region -----------------page navigation management-----------------
override public Page GotoPage(Book bk, Page currentPage, int step)
{
Page result = base.GotoPage( bk, currentPage, step );
if (step == 1)
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, (ThreadStart)delegate
{
PreparePageCache(result);
});
}
return result;
}
public bool CheckPageRange(Book bk, Page currentPage, int step)
{
int pos = 0;
if (currentPage != null)
pos = bk.Pages.IndexOf(currentPage);
if (step == -1)
{
return (pos == 0) ? false : true;
}
else
{
return (pos >= bk.Pages.Count - 1) ? false : true;
}
}
public void GotoFrame(Book bk, ref Page currentPage, ref Zone currentFrame, int step)
{
currentFrame = GotoFrame(currentPage, currentFrame, step);
//we reach the min or max, change the page
if (currentFrame == null)
currentPage = GotoPage(bk, currentPage, step);
GotoFrame(currentPage, currentFrame, step);
}
internal Zone GotoFrame(Page currentPage, Zone currentFrame, int step)
{
//if no frames, return a page frame
if (currentPage.Frames.Count <= 0)
{
if (currentFrame == null)
return new Zone() { Duration = 15, OrderNum = 0, X = 0, Y = 0, Type = FrameType.Page };
else
return null;
}
//search current frame position
int posFrame = 0;
if (currentFrame != null)
posFrame = currentPage.Frames.IndexOf(currentFrame);
//we go back, manage the min
if (step == -1)
{
return (posFrame == 0) ? null : currentPage.Frames[posFrame - 1];
}
else //we go forward, manage the max
{
return (posFrame >= currentPage.Frames.Count - 1) ? null : currentPage.Frames[posFrame + 1];
}
}
#endregion
override public long ManageCache(Book bk)
{
int counter = bk.Pages.Where(p => p.ImageExist == true).Count();
//select and delete if more than cache count
IEnumerable<Page> filter = bk.Pages
.Where(p => p.ImageExist == true)
.OrderBy(p => p.Index).Take(counter - WorkspaceService.Instance.Settings.ImageCacheCount);
foreach (Page item in filter)
item.Image = null;
//select and delete if over cache duration
filter = bk.Pages
.Where(p => p.ImageExist == true &&
p.ImageLastAcces < DateTime.Now.AddMinutes(-WorkspaceService.Instance.Settings.ImageCacheDuration))
.OrderBy(p => p.Index);
foreach (Page item in filter)
item.Image = null;
//return total size in cache
return bk.Pages.Where(p => p.ImageExist == true).Sum(f => f.Image.StreamSource.Length) / 1024 / 1024;
}
internal void PreparePageCache(object threadParam)
{
Page currentPage = threadParam as Page;
if (currentPage != null)
{
//load the 3 next pages after current page
BitmapImage tmpImage = null;
Page tmpPage = GetNextPage(currentPage);
if (tmpPage != null) tmpImage = tmpPage.Image;
tmpPage = GetNextPage(tmpPage);
if (tmpPage != null) tmpImage = tmpPage.Image;
tmpPage = GetNextPage(tmpPage);
if (tmpPage != null) tmpImage = tmpPage.Image;
}
}
internal Page GetNextPage(Page currentPage)
{
if (currentPage != null)
{
Book parent = currentPage.Parent;
if (parent != null)
{
int next = parent.Pages.IndexOf(currentPage);
if (next >= parent.Pages.Count - 1)
return null;
else
{
next = next + 1;
return parent.Pages[next];
}
}
else return null;
}
else return null;
}
}
}