text: package com.uwsoft.editor.proxy;
  
  import java.io.File;
  import java.io.FileNotFoundException;
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Iterator;
  import java.util.Map.Entry;
  
  import com.badlogic.gdx.graphics.Color;
  import com.badlogic.gdx.graphics.Pixmap;
  import com.badlogic.gdx.graphics.Texture;
  import com.uwsoft.editor.renderer.data.*;
  
  import org.apache.commons.io.FileUtils;
  import org.apache.commons.io.FilenameUtils;
  
  import com.badlogic.gdx.Files;
  import com.badlogic.gdx.Gdx;
  import com.badlogic.gdx.files.FileHandle;
  import com.badlogic.gdx.graphics.g2d.BitmapFont;
  import com.badlogic.gdx.graphics.g2d.ParticleEffect;
  import com.badlogic.gdx.graphics.g2d.TextureAtlas;
  import com.badlogic.gdx.graphics.g2d.TextureRegion;
  import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;
  import com.badlogic.gdx.graphics.glutils.ShaderProgram;
  import com.badlogic.gdx.utils.Json;
  import com.puremvc.patterns.proxy.BaseProxy;
  import com.uwsoft.editor.data.SpineAnimData;
  import com.uwsoft.editor.Overlap2DFacade;
  import com.uwsoft.editor.renderer.resources.FontSizePair;
  import com.uwsoft.editor.renderer.resources.IResourceRetriever;
  import com.uwsoft.editor.renderer.utils.MySkin;
  
  /**
   * Created by azakhary on 4/26/2015.
   */
  public class ResourceManager extends BaseProxy implements IResourceRetriever {
  
      public String packResolutionName = "orig";
  
      private static final String TAG = ResourceManager.class.getCanonicalName();
      public static final String NAME = TAG;
  
      private HashMap<String, ParticleEffect> particleEffects = new HashMap<String, ParticleEffect>(1);
      private TextureAtlas currentProjectAtlas;
  
      private HashMap<String, SpineAnimData> spineAnimAtlases = new HashMap<String, SpineAnimData>();
      private HashMap<String, TextureAtlas> spriteAnimAtlases = new HashMap<String, TextureAtlas>();
      private HashMap<String, FileHandle> spriterAnimFiles = new HashMap<String, FileHandle>();
      private HashMap<FontSizePair, BitmapFont> bitmapFonts = new HashMap<>();
      private HashMap<String, ShaderProgram> shaderPrograms = new HashMap<String, ShaderProgram>(1);
  
      private TextureRegion defaultRegion;
  
      private ResolutionManager resolutionManager;
  
      public ResourceManager() {
          super(NAME);
      }
  
      @Override
      public void onRegister() {
          super.onRegister();
          facade = Overlap2DFacade.getInstance();
          resolutionManager = facade.retrieveProxy(ResolutionManager.NAME);
  
          // TODO: substitute this with "NO IMAGE" icon
          Pixmap pixmap = new Pixmap(50, 50, Pixmap.Format.RGBA8888);
          pixmap.setColor(new Color(1, 1, 1, 0.4f));
          pixmap.fill();
          defaultRegion = new TextureRegion(new Texture(pixmap));
      }
  
      @Override
      public TextureRegion getTextureRegion(String name) {
          TextureRegion reg = currentProjectAtlas.findRegion(name);
  
          if(reg == null) {
              reg = defaultRegion;
          }
  
          return reg;
      }
  
      public TextureAtlas getTextureAtlas() {
          return currentProjectAtlas;
      }
  
      @Override
      public ParticleEffect getParticleEffect(String name) {
          return new ParticleEffect(particleEffects.get(name));
      }
  
  
      @Override
      public TextureAtlas getSkeletonAtlas(String animationName) {
          SpineAnimData animData = spineAnimAtlases.get(animationName);
          return animData.atlas;
      }
  
      /**
       * Sets working resolution, please set before doing any loading
       * @param resolution String resolution name, default is "orig" later use resolution names created in editor
       */
      public void setWorkingResolution(String resolution) {
          ResolutionEntryVO resolutionObject = getProjectVO().getResolution("resolutionName");
          if(resolutionObject != null) {
              packResolutionName = resolution;
          }
      }
  
  
      @Override
      public FileHandle getSkeletonJSON(String animationName) {
          SpineAnimData animData = spineAnimAtlases.get(animationName);
          return animData.jsonFile;
      }
  
      @Override
      public FileHandle getSCMLFile(String name) {
          return spriterAnimFiles.get(name);
      }
  
  
      @Override
      public TextureAtlas getSpriteAnimation(String animationName) {
          return spriteAnimAtlases.get(animationName);
      }
  
  
      @Override
      public BitmapFont getBitmapFont(String fontName, int fontSize) {
          FontSizePair pair = new FontSizePair(fontName, fontSize);
          return bitmapFonts.get(pair);
      }
  
  
      @Override
      public MySkin getSkin() {
          //return textureManager.projectSkin;
          // not sure if we are going to use skins for labels
          return null;
      }
  
      @Override
      public ProjectInfoVO getProjectVO() {
          ProjectManager projectManager = facade.retrieveProxy(ProjectManager.NAME);
          return projectManager.getCurrentProjectInfoVO();
      }
  
  
      @Override
      public SceneVO getSceneVO(String name) {
          SceneDataManager sceneDataManager = facade.retrieveProxy(SceneDataManager.NAME);
          // TODO: this should be cached
          FileHandle file = Gdx.files.internal(sceneDataManager.getCurrProjectScenePathByName(name));
          Json json = new Json();
          json.setIgnoreUnknownFields(true);
          return json.fromJson(SceneVO.class, file.readString());
      }
  
      public void loadCurrentProjectData(String projectPath, String curResolution) {
          packResolutionName = curResolution;
          loadCurrentProjectAssets(projectPath + "/assets/" + curResolution + "/pack/pack.atlas");
          loadCurrentProjectSkin(projectPath + "/assets/orig/styles");
          loadCurrentProjectParticles(projectPath + "/assets/orig/particles");
          loadCurrentProjectSpineAnimations(projectPath + "/assets/", curResolution);
          loadCurrentProjectSpriteAnimations(projectPath + "/assets/", curResolution);
          loadCurrentProjectSpriterAnimations(projectPath + "/assets/", curResolution);
          loadCurrentProjectBitmapFonts(projectPath, curResolution);
          loadCurrentProjectShaders(projectPath + "/assets/shaders/");
      }
  
      private void loadCurrentProjectParticles(String path) {
          particleEffects.clear();
          FileHandle sourceDir = new FileHandle(path);
          for (FileHandle entry : sourceDir.list()) {
              File file = entry.file();
              String filename = file.getName();
              if (file.isDirectory() || filename.endsWith(".DS_Store")) continue;
  
              ParticleEffect particleEffect = new ParticleEffect();
              particleEffect.load(Gdx.files.internal(file.getAbsolutePath()), currentProjectAtlas, "");
              particleEffects.put(filename, particleEffect);
          }
  
      }
  
  
      private void loadCurrentProjectSpineAnimations(String path, String curResolution) {
          spineAnimAtlases.clear();
          FileHandle sourceDir = new FileHandle(path + "orig/spine-animations");
          for (FileHandle entry : sourceDir.list()) {
              if (entry.file().isDirectory()) {
                  String animName = FilenameUtils.removeExtension(entry.file().getName());
                  TextureAtlas atlas = new TextureAtlas(Gdx.files.internal(path + curResolution + "/spine-animations/" + File.separator + animName + File.separator + animName + ".atlas"));
                  FileHandle animJsonFile = Gdx.files.internal(entry.file().getAbsolutePath() + File.separator + animName + ".json");
                  SpineAnimData data = new SpineAnimData();
                  data.atlas = atlas;
                  data.jsonFile = animJsonFile;
                  data.animName = animName;
                  spineAnimAtlases.put(animName, data);
              }
          }
  
      }
  
      private void loadCurrentProjectSpriteAnimations(String path, String curResolution) {
          spriteAnimAtlases.clear();
          FileHandle sourceDir = new FileHandle(path + curResolution + "/sprite-animations");
          for (FileHandle entry : sourceDir.list()) {
              if (entry.file().isDirectory()) {
                  String animName = FilenameUtils.removeExtension(entry.file().getName());
                  TextureAtlas atlas = new TextureAtlas(Gdx.files.internal(entry.file().getAbsolutePath() + File.separator + animName + ".atlas"));
                  spriteAnimAtlases.put(animName, atlas);
              }
          }
      }
  
      private void loadCurrentProjectSpriterAnimations(String path, String curResolution) {
          spriterAnimFiles.clear();
          FileHandle sourceDir = new FileHandle(path + "orig" + "/spriter-animations");
          for (FileHandle entry : sourceDir.list()) {
              if (entry.file().isDirectory()) {
                  String animName = entry.file().getName();
                  FileHandle scmlFile = new FileHandle(path + "orig" + "/spriter-animations/" + animName + "/" + animName + ".scml");
                  spriterAnimFiles.put(animName, scmlFile);
              }
          }
      }
  
      public void loadCurrentProjectAssets(String packPath) {
          try {
              currentProjectAtlas = new TextureAtlas(Gdx.files.getFileHandle(packPath, Files.FileType.Internal));
          } catch (Exception e) {
              currentProjectAtlas = new TextureAtlas();
          }
      }
  
      public ArrayList<FontSizePair> getProjectRequiredFontsList() {
          HashSet<FontSizePair> fontsToLoad = new HashSet<>();
  
          for (int i = 0; i < getProjectVO().scenes.size(); i++) {
              SceneVO scene = getSceneVO(getProjectVO().scenes.get(i).sceneName);
              CompositeVO composite = scene.composite;
              if (composite == null) {
                  continue;
              }
              FontSizePair[] fonts = composite.getRecursiveFontList();
              for (CompositeItemVO library : getProjectVO().libraryItems.values()) {
                  FontSizePair[] libFonts = library.composite.getRecursiveFontList();
                  Collections.addAll(fontsToLoad, libFonts);
              }
              Collections.addAll(fontsToLoad, fonts);
          }
  
          return new ArrayList<>(fontsToLoad);
      }
  
      public void loadCurrentProjectBitmapFonts(String path, String curResolution) {
          bitmapFonts.clear();
  
          ArrayList<FontSizePair> requiredFonts = getProjectRequiredFontsList();
          for (int i = 0; i < requiredFonts.size(); i++) {
              FontSizePair pair = requiredFonts.get(i);
              FileHandle fontFile;
              try {
                  fontFile = getTTFSafely(pair.fontName);
                  FreeTypeFontGenerator generator = new FreeTypeFontGenerator(fontFile);
                  FreeTypeFontGenerator.FreeTypeFontParameter parameter = new FreeTypeFontGenerator.FreeTypeFontParameter();
                  parameter.size = Math.round(pair.fontSize * resolutionManager.getCurrentMul());
                  BitmapFont font = generator.generateFont(parameter);
                  bitmapFonts.put(pair, font);
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      }
      
      private void loadCurrentProjectShaders(String path) {
      	Iterator<Entry<String, ShaderProgram>> it = shaderPrograms.entrySet().iterator();
      	while (it.hasNext()) {
      		Entry<String, ShaderProgram> pair = it.next();
      		pair.getValue().dispose();
      		it.remove(); 
      	}
          shaderPrograms.clear();
          FileHandle sourceDir = new FileHandle(path);
          for (FileHandle entry : sourceDir.list()) {
              File file = entry.file();
              String filename = file.getName().replace(".vert", "").replace(".frag", "");
              if (file.isDirectory() || filename.endsWith(".DS_Store") || shaderPrograms.containsKey(filename)) continue;
              // check if pair exists.
              if(Gdx.files.internal(path + filename + ".vert").exists() && Gdx.files.internal(path + filename + ".frag").exists()) {
                  ShaderProgram shaderProgram = new ShaderProgram(Gdx.files.internal(path + filename + ".vert"), Gdx.files.internal(path + filename + ".frag"));
                  System.out.println(shaderProgram.getLog());
                  shaderPrograms.put(filename, shaderProgram);
              }
          }
  
      }
  
      /**
       * @param fontPath
       * @deprecated
       */
      private void loadCurrentProjectSkin(String fontPath) {
          /*
          File styleFile = new File(fontPath, "styles.dt");
          FileHandle f = new FileHandle(styleFile);
  
          if (styleFile.isFile() && styleFile.exists()) {
              projectSkin = new MySkin(f);
              ObjectMap<String, BitmapFont> map = projectSkin.getAll(BitmapFont.class);
              for (ObjectMap.Entry<String, BitmapFont> entry : map.entries()) {
                  projectSkin.getFont(entry.key).getRegion().getTexture().setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
              }
          }
          */
      }
  
      public FileHandle getTTFSafely(String fontName) throws IOException {
          FontManager fontManager = facade.retrieveProxy(FontManager.NAME);
  
          ProjectManager projectManager = facade.retrieveProxy(ProjectManager.NAME);
          String expectedPath = projectManager.getFreeTypeFontPath() + File.separator + fontName + ".ttf";
          FileHandle expectedFile = Gdx.files.internal(expectedPath);
          if (!expectedFile.exists()) {
              // let's check if system fonts fot it
              HashMap<String, String> fonts = fontManager.getFontsMap();
              if (fonts.containsKey(fontName)) {
                  File source = new File(fonts.get(fontName));
                  FileUtils.copyFile(source, expectedFile.file());
                  expectedFile = Gdx.files.internal(expectedPath);
              } else {
                  throw new FileNotFoundException();
              }
          }
  
          return expectedFile;
      }
  
      public void addBitmapFont(String name, int size, BitmapFont font) {
          bitmapFonts.put(new FontSizePair(name, size), font);
      }
  
      public void flushAllUnusedFonts() {
          //List of fonts that are required to be in memory
          ArrayList<FontSizePair> requiredFonts = getProjectRequiredFontsList();
          ArrayList<FontSizePair> fontsInMemory = new ArrayList<>(bitmapFonts.keySet());
  
          for (FontSizePair font : fontsInMemory) {
              if (!requiredFonts.contains(font)) {
                  bitmapFonts.remove(font);
              }
          }
      }
  
      public boolean isFontLoaded(String shortName, int fontSize) {
          return bitmapFonts.containsKey(new FontSizePair(shortName, fontSize));
      }
  
      public void prepareEmbeddingFont(String fontfamily, int fontSize) {
          flushAllUnusedFonts();
  
          if (isFontLoaded(fontfamily, fontSize)) {
              return;
          }
  
          FontManager fontManager = facade.retrieveProxy(FontManager.NAME);
  
          FreeTypeFontGenerator.FreeTypeFontParameter parameter = new FreeTypeFontGenerator.FreeTypeFontParameter();
          parameter.size = fontSize;
  
          FreeTypeFontGenerator generator = new FreeTypeFontGenerator(fontManager.getTTFByName(fontfamily));
          BitmapFont font = generator.generateFont(parameter);
  
          addBitmapFont(fontfamily, parameter.size, font);
      }
  
      public HashMap<String, SpineAnimData> getProjectSpineAnimationsList() {
          return spineAnimAtlases;
      }
  
      public HashMap<String, TextureAtlas> getProjectSpriteAnimationsList() {
          return spriteAnimAtlases;
      }
  
      public HashMap<String, FileHandle> getProjectSpriterAnimationsList() {
          return spriterAnimFiles;
      }
  
      public TextureAtlas getProjectAssetsList() {
          return currentProjectAtlas;
      }
  
      public HashMap<String, ParticleEffect> getProjectParticleList() {
          return particleEffects;
      }
  
      @Override
      public ResolutionEntryVO getLoadedResolution() {
          if(packResolutionName.equals("orig")) {
              return getProjectVO().originalResolution;
          }
          return getProjectVO().getResolution(packResolutionName);
      }
  
  	@Override
  	public ShaderProgram getShaderProgram(String shaderName) {
  		return shaderPrograms.get(shaderName);
  	}
  
      public HashMap<String, ShaderProgram> getShaders() {
          return shaderPrograms;
      }
  }
  