using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Themes;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Themes;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace MediaBrowser.Server.Implementations.Themes
{
    public class AppThemeManager : IAppThemeManager
    {
        private readonly IServerApplicationPaths _appPaths;
        private readonly IFileSystem _fileSystem;
        private readonly IJsonSerializer _json;
        private readonly ILogger _logger;

        private readonly string[] _supportedImageExtensions = { ".png", ".jpg", ".jpeg" };

        public AppThemeManager(IServerApplicationPaths appPaths, IFileSystem fileSystem, IJsonSerializer json, ILogger logger)
        {
            _appPaths = appPaths;
            _fileSystem = fileSystem;
            _json = json;
            _logger = logger;
        }

        private string ThemePath
        {
            get
            {
                return Path.Combine(_appPaths.ProgramDataPath, "appthemes");
            }
        }

        private string GetThemesPath(string applicationName)
        {
            if (string.IsNullOrWhiteSpace(applicationName))
            {
                throw new ArgumentNullException("applicationName");
            }

            // Force everything lowercase for consistency and maximum compatibility with case-sensitive file systems
            var name = _fileSystem.GetValidFilename(applicationName.ToLower());

            return Path.Combine(ThemePath, name);
        }

        private string GetThemePath(string applicationName, string name)
        {
            if (string.IsNullOrWhiteSpace(name))
            {
                throw new ArgumentNullException("name");
            }
            
            // Force everything lowercase for consistency and maximum compatibility with case-sensitive file systems
            name = _fileSystem.GetValidFilename(name.ToLower());

            return Path.Combine(GetThemesPath(applicationName), name);
        }

        private string GetImagesPath(string applicationName, string themeName)
        {
            return Path.Combine(GetThemePath(applicationName, themeName), "images");
        }

        public IEnumerable<AppThemeInfo> GetThemes(string applicationName)
        {
            var path = GetThemesPath(applicationName);

            try
            {
                return Directory
                    .EnumerateFiles(path, "*", SearchOption.AllDirectories)
                    .Where(i => string.Equals(Path.GetExtension(i), ".json", StringComparison.OrdinalIgnoreCase))
                    .Select(i =>
                    {
                        try
                        {
                            return _json.DeserializeFromFile<AppThemeInfo>(i);
                        }
                        catch (Exception ex)
                        {
                            _logger.ErrorException("Error deserializing {0}", ex, i);
                            return null;
                        }

                    }).Where(i => i != null);
            }
            catch (DirectoryNotFoundException)
            {
                return new List<AppThemeInfo>();
            }
        }

        public AppTheme GetTheme(string applicationName, string name)
        {
            var themePath = GetThemePath(applicationName, name);
            var file = Path.Combine(themePath, "theme.json");

            var imagesPath = GetImagesPath(applicationName, name);

            var theme = _json.DeserializeFromFile<AppTheme>(file);

            theme.Images = new DirectoryInfo(imagesPath)
                .EnumerateFiles("*", SearchOption.TopDirectoryOnly)
                .Where(i => _supportedImageExtensions.Contains(i.Extension, StringComparer.OrdinalIgnoreCase))
                .Select(GetThemeImage)
                .ToList();

            return theme;
        }

        private ThemeImage GetThemeImage(FileInfo file)
        {
            var dateModified = _fileSystem.GetLastWriteTimeUtc(file);

            var cacheTag = (file.FullName + dateModified.Ticks).GetMD5().ToString("N");

            return new ThemeImage
            {
                CacheTag = cacheTag,
                Name = file.Name
            };
        }

        public void SaveTheme(AppTheme theme)
        {
            var themePath = GetThemePath(theme.AppName, theme.Name);
            var file = Path.Combine(themePath, "theme.json");

            Directory.CreateDirectory(themePath);

            // Clone it so that we don't serialize all the images - they're always dynamic
            var clone = new AppTheme
            {
                AppName = theme.AppName,
                Name = theme.Name,
                Options = theme.Options,
                Images = null
            };

            _json.SerializeToFile(clone, file);
        }

        public InternalThemeImage GetImageImageInfo(string applicationName, string themeName, string imageName)
        {
            var imagesPath = GetImagesPath(applicationName, themeName);

            var file = new DirectoryInfo(imagesPath).EnumerateFiles("*", SearchOption.TopDirectoryOnly)
                .First(i => string.Equals(i.Name, imageName, StringComparison.OrdinalIgnoreCase));

            var themeImage = GetThemeImage(file);

            return new InternalThemeImage
            {
                CacheTag = themeImage.CacheTag,
                Name = themeImage.Name,
                Path = file.FullName,
                DateModified = _fileSystem.GetLastWriteTimeUtc(file)
            };
        }
    }
}
