content: package me.sat7.dynamicshop.utilities;
  
  import me.sat7.dynamicshop.DynamicShop;
  import me.sat7.dynamicshop.constants.Constants;
  import me.sat7.dynamicshop.files.CustomConfig;
  import org.apache.commons.io.FileUtils;
  import org.apache.commons.io.FilenameUtils;
  import org.bukkit.Bukkit;
  import org.bukkit.inventory.meta.ItemMeta;
  
  import java.io.File;
  import java.io.IOException;
  import java.nio.file.Files;
  import java.util.HashMap;
  import java.util.concurrent.ConcurrentHashMap;
  
  import static me.sat7.dynamicshop.utilities.HashUtil.CreateHashString;
  import static me.sat7.dynamicshop.utilities.LangUtil.t;
  import static me.sat7.dynamicshop.utilities.MathUtil.MilliSecondsToTick;
  import static me.sat7.dynamicshop.utilities.MathUtil.TickToMilliSeconds;
  
  public final class RotationUtil
  {
      public static final String rotationFolderName = "Rotation";
      public static final String rotationDataPath = DynamicShop.plugin.getDataFolder() + "/" + rotationFolderName;
  
      // ===== [ Private ] =====
  
      public static final HashMap<String, CustomConfig[]> rotationDataMap = new HashMap<>();
      private static final HashMap<String, CustomConfig> sharedDataMap = new HashMap<>();
  
      // 현재 상점 yml 데이터 기반으로 로테이션 yml 파일을 생성함
      private static CustomConfig CreateRotationDataFromCurrentShop(String shopName, int rotationIndex)
      {
          CustomConfig shopData = ShopUtil.shopConfigFiles.get(shopName);
          if (shopData == null)
              return null;
  
          CustomConfig rotationData = new CustomConfig();
          rotationData.setup(shopName + "_" + (rotationIndex + 1), rotationFolderName + "/" + shopName);
          rotationData.get().set("Options.title", shopData.get().getString("Options.title", shopName));
          rotationData.get().set("Options.lore", shopData.get().getString("Options.lore", ""));
          rotationData.get().set("Options.page", shopData.get().getInt("Options.page", 1));
  
          // 기존의 데이터를 삭제함
          for (String s : rotationData.get().getKeys(false))
          {
              try
              {
                  Integer.parseInt(s); // 아이템 데이터만 가져오기 위함
                  rotationData.get().set(s, null);
              } catch (Exception ignore)
              {
  
              }
          }
  
          // 상점 아이템들을 로테이션 데이터에 기록
          for (String s : shopData.get().getKeys(false))
          {
              try
              {
                  Integer.parseInt(s); // 아이템 데이터만 가져오기 위함
  
                  rotationData.get().set(s + ".mat", shopData.get().get(s + ".mat"));
                  rotationData.get().set(s + ".itemStack", shopData.get().get(s + ".itemStack"));
  
                  // merchandise
                  if (shopData.get().contains(s + ".value"))
                  {
                      rotationData.get().set(s + ".type", "item");
                  }
                  // deco
                  else
                  {
                      rotationData.get().set(s + ".type", "deco");
                  }
              } catch (Exception ignore)
              {
              }
          }
  
          rotationData.save();
  
          return rotationData;
      }
  
      // 아이템 없이 빈 로테이션 yml 파일을 생성함
      private static CustomConfig CreateEmptyRotationData(String shopName, int rotationIndex)
      {
          CustomConfig shopData = ShopUtil.shopConfigFiles.get(shopName);
          if (shopData == null)
              return null;
  
          CustomConfig rotationData = new CustomConfig();
          rotationData.setup(shopName + "_" + (rotationIndex + 1), rotationFolderName + "/" + shopName);
          rotationData.get().set("Options.title", shopData.get().getString("Options.title", shopName));
          rotationData.get().set("Options.lore", shopData.get().getString("Options.lore", ""));
          rotationData.get().set("Options.page", shopData.get().getInt("Options.page", 1));
          rotationData.save();
  
          return rotationData;
      }
  
      // 공용 데이터 반환 (재고, 가격, 미디안 등을 저장)
      private static CustomConfig GetSharedData(String shopName)
      {
          if (sharedDataMap.containsKey(shopName))
          {
              return sharedDataMap.get(shopName);
          } else
          {
              CustomConfig data = new CustomConfig();
              data.setup("SharedData", rotationFolderName + "/" + shopName);
              data.get().options().header("Do NOT edit this file manually.");
  
  
  
              data.get().options().copyDefaults(true);
              data.save();
  
              sharedDataMap.put(shopName, data);
  
              return data;
          }
      }
  
      // 공용 데이터에서 필요 없는 데이터 제거 (어느 로테이션 파일에도 없는 아이템들)
      private static void CleanupSharedData(String shopName)
      {
          CustomConfig[] rotationDataList = rotationDataMap.get(shopName);
          if (rotationDataList == null)
              return;
  
          CustomConfig sharedData = GetSharedData(shopName);
          if (sharedData == null)
              return;
  
          for (String s : sharedData.get().getKeys(false))
          {
              try
              {
                  String matString = sharedData.get().getString(s + ".mat");
                  ItemMeta meta = (ItemMeta) sharedData.get().get(s + ".itemStack");
  
                  boolean isExist = false;
                  for (CustomConfig rotation : rotationDataList)
                  {
                      if (rotation == null)
                          continue;
  
                      for (String itemIdx : rotation.get().getKeys(false))
                      {
                          try
                          {
                              Integer.parseInt(itemIdx);
  
                              String otherMat = rotation.get().getString(itemIdx + ".mat");
                              ItemMeta otherMeta = (ItemMeta) rotation.get().get(itemIdx + ".itemStack");
  
                              if (matString.equals(otherMat))
                              {
                                  if (meta != null && meta.equals(otherMeta))
                                  {
                                      isExist = true;
                                      break;
                                  } else if (otherMeta == null)
                                  {
                                      isExist = true;
                                      break;
                                  }
                              }
                          } catch (Exception ignore)
                          {
                          }
                      }
  
                      if (isExist)
                          break;
                  }
  
                  if (!isExist)
                  {
                      sharedData.get().set(s, null);
                  }
              } catch (Exception ignore)
              {
              }
          }
  
          sharedData.save();
      }
  
      // ===== [ Public ] =====
  
      // 원래 있는 로테이션 yml 파일들을 읽음
      public static void ReadRotationYMLFiles(String shopName)
      {
          rotationDataMap.remove(shopName);
  
          File rotationFolder = new File(rotationDataPath);
          rotationFolder.mkdir();
          File subFolder = new File(rotationDataPath, shopName);
          subFolder.mkdir();
  
          CustomConfig[] rotationDataList = new CustomConfig[7];
  
          String path = rotationDataPath + "/" + shopName;
          File[] listOfFiles = new File(path).listFiles();
  
          if (listOfFiles == null)
              return;
  
          for (File f : listOfFiles)
          {
              if (!f.getName().contains(shopName))
                  continue;
  
              String[] temp = FilenameUtils.getBaseName(f.getName()).split("_");
              if (temp.length != 2)
                  continue;
  
              try
              {
                  int idx = Integer.parseInt(temp[1]) - 1;
                  if(idx < 0 || idx >= 7)
                      continue;
  
                  CustomConfig cc = new CustomConfig();
                  cc.setup(f.getName().replace(".yml", ""), rotationFolderName + "/" + shopName);
                  rotationDataList[idx] = cc;
              } catch (Exception ignore)
              {
  
              }
          }
  
          rotationDataMap.put(shopName, rotationDataList);
      }
  
      // 로테이션 YML 파일의 수 반환
      public static int GetRotationYmlFileCount(String shopName)
      {
          if (!rotationDataMap.containsKey(shopName))
              return 0;
  
          int count = 0;
          CustomConfig[] data = rotationDataMap.get(shopName);
          for (CustomConfig cc : data)
          {
              if (cc != null)
                  count++;
          }
  
          return count;
      }
  
      // 새 로테이션 데이터를 추가
      public static void AddNewRotationData(String shopName, int rotationIndex, boolean isEmpty)
      {
          CustomConfig data;
          if (isEmpty)
          {
              data = CreateEmptyRotationData(shopName, rotationIndex);
          } else
          {
              data = CreateRotationDataFromCurrentShop(shopName, rotationIndex);
          }
  
          if (!rotationDataMap.containsKey(shopName))
          {
              CustomConfig[] dataList = new CustomConfig[7];
              dataList[rotationIndex] = data;
              rotationDataMap.put(shopName, dataList);
          } else
          {
              rotationDataMap.get(shopName)[rotationIndex] = data;
          }
      }
  
      // 현재 로테이션 인덱스 반환
      public static int GetCurrentRotationIndex(String shopName)
      {
          if (!ShopUtil.shopConfigFiles.containsKey(shopName))
              return -1;
  
          CustomConfig shopData = ShopUtil.shopConfigFiles.get(shopName);
          if (shopData == null)
              return -1;
  
          return shopData.get().getInt("Options.Rotation.Current", -1);
      }
  
      // 로테이션 데이터를 상점 데이터에 맞춰 갱신함
      public static void UpdateCurrentRotationData(String shopName, int tradeIdx)
      {
          int currentRotationIndex = GetCurrentRotationIndex(shopName);
          if (currentRotationIndex == -1)
              return;
  
          CustomConfig shopData = ShopUtil.shopConfigFiles.get(shopName);
          if (shopData == null)
              return;
  
          CustomConfig[] rotationYmlFiles = rotationDataMap.get(shopName);
          if (rotationYmlFiles == null)
              return;
  
          CustomConfig rotationData = rotationYmlFiles[currentRotationIndex];
          if (rotationData == null)
              return;
  
          if (shopData.get().contains(String.valueOf(tradeIdx)))
          {
              rotationData.get().set(tradeIdx + ".mat", shopData.get().get(tradeIdx + ".mat"));
              rotationData.get().set(tradeIdx + ".itemStack", shopData.get().get(tradeIdx + ".itemStack"));
  
              // merchandise
              if (shopData.get().contains(tradeIdx + ".value"))
              {
                  rotationData.get().set(tradeIdx + ".type", "item");
              }
              // deco
              else
              {
                  rotationData.get().set(tradeIdx + ".type", "deco");
              }
          } else
          {
              rotationData.get().set(String.valueOf(tradeIdx), null);
          }
  
          rotationData.save();
      }
  
      // 공용 데이터 갱신 (상점이 로테이션 되기 직전에 호출되야함)
      public static void SaveSharedData(String shopName)
      {
          CustomConfig sharedData = GetSharedData(shopName);
          if (sharedData == null)
              return;
  
          CustomConfig shopData = ShopUtil.shopConfigFiles.get(shopName);
          if (shopData == null)
              return;
  
          for (String s : shopData.get().getKeys(false))
          {
              try
              {
                  Integer.parseInt(s); // 아이템 데이터만 가져오기 위함
  
                  // merchandise
                  if (shopData.get().contains(s + ".value"))
                  {
                      String mat = shopData.get().getString(s + ".mat");
                      String meta = shopData.get().getString(s + ".itemStack");
                      String hash = CreateHashString(mat, meta);
  
                      sharedData.get().set(hash, shopData.get().get(s));
                  }
              } catch (Exception ignore)
              {
              }
          }
  
          sharedData.save();
      }
  
      // 다음 로테이션 적용
      public static void ApplyNextRotation(String shopName, long period)
      {
          CustomConfig shopData = ShopUtil.shopConfigFiles.get(shopName);
          if (shopData == null)
          {
              StopRotationTask(shopName);
              DynamicShop.console.sendMessage(Constants.DYNAMIC_SHOP_PREFIX + " Rotation task stopped because the shop does not exist. shopName: " + shopName);
              return;
          }
  
          int dataCount = GetRotationYmlFileCount(shopName);
          if (dataCount <= 0)
          {
              DisableRotation(shopName);
  
              DynamicShop.console.sendMessage(Constants.DYNAMIC_SHOP_PREFIX + " Stop rotation task due to missing files. shopName: " + shopName);
              return;
          }
  
          int oldIdx = GetCurrentRotationIndex(shopName);
          int newIdx = FindNextRotationIndex(shopName, oldIdx);
          if (newIdx == -1)
          {
              DisableRotation(shopName);
  
              DynamicShop.console.sendMessage(Constants.DYNAMIC_SHOP_PREFIX + " Stop rotation task due to missing files. shopName: " + shopName);
              return;
          }
  
          // 상점에 로테이션 적용
          ApplyRotation(shopName, oldIdx, newIdx);
  
          shopData.get().set("Options.Rotation.NextTimer", System.currentTimeMillis() + TickToMilliSeconds(period));
          shopData.get().set("Options.Rotation.Period", period);
          shopData.save();
  
          // 쉐어드 데이터에서 불필요한 내용 지움
          CleanupSharedData(shopName);
      }
  
      public static int FindNextRotationIndex(String shopName, int currentIndex)
      {
          if (GetRotationYmlFileCount(shopName) == 0)
              return -1;
  
          CustomConfig[] data = rotationDataMap.get(shopName);
          int searchIdx = currentIndex + 1;
          if (searchIdx >= 7)
              searchIdx = 0;
  
          for (int i = 0; i < 7; i++)
          {
              if (data[searchIdx] != null)
                  return searchIdx;
  
              searchIdx++;
              if (searchIdx >= 7)
                  searchIdx = 0;
          }
  
          return -1;
      }
  
      // 로테이션 적용
      public static void ApplyRotation(String shopName, int oldIdx, int rotationIdx)
      {
          CustomConfig shopData = ShopUtil.shopConfigFiles.get(shopName);
          CustomConfig[] rotationDatas = rotationDataMap.get(shopName);
          if (shopData == null || rotationDatas == null || rotationIdx == -1 || rotationDatas[rotationIdx] == null)
              return;
  
          // 지금 상점의 상태를 로테이션 데이터에 저장함.
          if(oldIdx != -1 && oldIdx != rotationIdx)
              CreateRotationDataFromCurrentShop(shopName, oldIdx);
  
          // 현재 상점의 데이터를 쉐어드 데이터에 저장
          SaveSharedData(shopName);
  
          // 상점을 비움
          for (String s : shopData.get().getKeys(false))
          {
              try
              {
                  Integer.parseInt(s); // 아이템 데이터만 가져오기 위함
                  shopData.get().set(s, null); // 기존 데이터를 다 삭제함.
              } catch (Exception ignore)
              {
  
              }
          }
  
          CustomConfig rotationData = rotationDatas[rotationIdx];
  
          // 로테이션 전용 타이틀과 로어 적용
          rotationData.reload();
          shopData.get().set("Options.title", rotationData.get().getString("Options.title", shopName));
          shopData.get().set("Options.lore", rotationData.get().getString("Options.lore", ""));
          shopData.get().set("Options.page", rotationData.get().getInt("Options.page", 1));
  
          // 쉐어드 데이터 기반으로 상품을 다시 채워넣음
          CustomConfig sharedData = new CustomConfig();
          sharedData.setup("SharedData", rotationFolderName + "/" + shopName);
  
          int missingDataCount = 0;
  
          for (String slotIndex : rotationData.get().getKeys(false))
          {
              try
              {
                  Integer.parseInt(slotIndex); // 아이템 데이터만 가져오기 위함
  
                  if (rotationData.get().getString(slotIndex + ".type").equals("item"))
                  {
                      String mat = rotationData.get().getString(slotIndex + ".mat");
                      String meta = rotationData.get().getString(slotIndex + ".itemStack");
                      String hash = CreateHashString(mat, meta);
                      if (sharedData.get().contains(hash))
                      {
                          shopData.get().set(slotIndex, sharedData.get().get(hash));
                      }
                      else
                      {
                          shopData.get().set(slotIndex + ".mat", rotationData.get().getString(slotIndex + ".mat"));
                          shopData.get().set(slotIndex + ".itemStack", rotationData.get().getString(slotIndex + ".itemStack"));
                          missingDataCount++;
                      }
                  } else
                  {
                      shopData.get().set(slotIndex + ".mat", rotationData.get().get(slotIndex + ".mat"));
                  }
              } catch (Exception ignore)
              {
  
              }
          }
  
          if (missingDataCount != 0)
          {
              DynamicShop.console.sendMessage(Constants.DYNAMIC_SHOP_PREFIX + t(null, "MESSAGE.ROTATION_SHARED_DATA_MISSING").replace("{0}", shopName).replace("{1}", String.valueOf(missingDataCount)));
          }
  
          shopData.get().set("Options.Rotation.Current", rotationIdx);
          shopData.save();
  
          //DynamicShop.console.sendMessage("로테이션 적용됨: " + shopName + " / " + rotationIdx + "번");
      }
  
      public static void DisableRotation(String shopName)
      {
          CustomConfig shopData = ShopUtil.shopConfigFiles.get(shopName);
          if (shopData != null)
          {
              shopData.get().set("Options.Rotation", null);
              shopData.save();
          }
  
          StopRotationTask(shopName);
  
          rotationDataMap.remove(shopName);
      }
  
      // ===== [ Task ] =====
  
      public static final ConcurrentHashMap<String, Integer> RotationTaskMap = new ConcurrentHashMap<>();
  
      public static void StartRotationTask(String shopName, long delay, long period)
      {
          StopRotationTask(shopName);
  
          Integer i = Bukkit.getScheduler().runTaskTimer(DynamicShop.plugin, () -> ApplyNextRotation(shopName, period), delay, period).getTaskId();
          RotationTaskMap.put(shopName, i);
  
          //DynamicShop.console.sendMessage("로테이션 시작됨: " + shopName + " / 딜레이: " + (delay / 20) + " / 간격: " + (period / 20));
      }
  
      private static void StopRotationTask(String shopName)
      {
          if (RotationTaskMap.containsKey(shopName))
          {
              Bukkit.getScheduler().cancelTask(RotationTaskMap.get(shopName));
              RotationTaskMap.remove(shopName);
          }
      }
  
      public static void Reload()
      {
          for (String s : RotationTaskMap.keySet())
          {
              sharedDataMap.remove(s);
              rotationDataMap.remove(s);
          }
  
          RestartAllRotationTask();
      }
  
      public static void RestartAllRotationTask()
      {
          for (String s : RotationTaskMap.keySet())
          {
              StopRotationTask(s);
          }
  
          RotationTaskMap.clear();
  
          for (String shopName : ShopUtil.shopConfigFiles.keySet())
          {
              RestartRotationTask(shopName);
          }
      }
  
      public static void RestartRotationTask(String shopName)
      {
          CustomConfig shopData = ShopUtil.shopConfigFiles.get(shopName);
          if (shopData == null)
              return;
  
          if (shopData.get().contains("Options.Rotation"))
          {
              ReadRotationYMLFiles(shopName);
              GetSharedData(shopName); // 없다면 새로 생성하게됨.
  
              long currentTime = System.currentTimeMillis();
              long timeLeft = shopData.get().getLong("Options.Rotation.NextTimer", currentTime) - currentTime;
              timeLeft = MilliSecondsToTick(timeLeft);
              if (timeLeft < 0)
              {
                  timeLeft = 0;
              }
              else
              {
                  int rotIdx = shopData.get().getInt("Options.Rotation.Current");
                  ApplyRotation(shopName, rotIdx, rotIdx);
              }
  
              StartRotationTask(shopName, timeLeft, shopData.get().getLong("Options.Rotation.Period", 20 * 60 * 60 * 24));
          }
      }
  
      // ===== [ Event ] =====
  
      public static void OnShopNameChanged(String oldName, String newName)
      {
          StopRotationTask(oldName);
  
          sharedDataMap.remove(oldName);
          rotationDataMap.remove(oldName);
  
          String oldPath = rotationDataPath + "/" + oldName;
          File subFolder = new File(oldPath);
          if (subFolder.exists())
          {
              File[] listOfFiles = new File(oldPath).listFiles();
              if (listOfFiles != null)
              {
                  File newFolder = new File(rotationDataPath, newName);
                  newFolder.mkdir();
  
                  boolean err = false;
                  for (File f : listOfFiles)
                  {
                      File dest = new File(f.getPath().replace(oldName, newName));
  
                      if (!f.renameTo(dest))
                      {
                          DynamicShop.console.sendMessage(Constants.DYNAMIC_SHOP_PREFIX + " Failed to copy file : " + f.getPath());
                          err = true;
                      }
                  }
  
                  if (!err)
                  {
                      try
                      {
                          FileUtils.deleteDirectory(new File(oldPath));
                      } catch (IOException e)
                      {
                          DynamicShop.console.sendMessage(Constants.DYNAMIC_SHOP_PREFIX + " Failed to delete folder : " + oldPath);
                      }
                  }
              }
          }
  
          ReadRotationYMLFiles(newName);
          GetSharedData(newName);
  
          RestartRotationTask(newName);
      }
  
      public static void OnShopCopy(String oldName, String newName)
      {
          String oldPath = rotationDataPath + "/" + oldName;
          File subFolder = new File(oldPath);
          if (subFolder.exists())
          {
              File[] listOfFiles = new File(oldPath).listFiles();
              if (listOfFiles != null)
              {
                  File newFolder = new File(rotationDataPath, newName);
                  newFolder.mkdir();
  
                  for (File f : listOfFiles)
                  {
                      try
                      {
                          File dest = new File(f.getPath().replace(oldName, newName));
                          Files.copy(f.toPath(), dest.toPath());
                      }
                      catch (Exception e)
                      {
                          DynamicShop.console.sendMessage(Constants.DYNAMIC_SHOP_PREFIX + " Failed to copy file : " + f.getPath());
                      }
                  }
              }
          }
  
          RestartRotationTask(newName);
      }
  
      public static void OnShopDeleted(String shopName)
      {
          StopRotationTask(shopName);
  
          sharedDataMap.remove(shopName);
          rotationDataMap.remove(shopName);
  
          String path = rotationDataPath + "/" + shopName;
  
          try
          {
              FileUtils.deleteDirectory(new File(path));
          } catch (IOException e)
          {
              DynamicShop.console.sendMessage(Constants.DYNAMIC_SHOP_PREFIX + " Failed to delete folder : " + path);
          }
      }
  
      public static void DeleteRotationFile(String shopName, int rotationIdx)
      {
          if (!rotationDataMap.containsKey(shopName))
              return;
  
          CustomConfig data = rotationDataMap.get(shopName)[rotationIdx];
          if (data != null)
              data.delete();
  
          rotationDataMap.get(shopName)[rotationIdx] = null;
      }
  
      public static void OnRotationFileSlotMoved(String shopName, int oldIdx, int newIdx)
      {
          CustomConfig[] data = rotationDataMap.get(shopName);
          if(data == null)
              return;
  
          if (data[oldIdx] == null || data[newIdx] != null)
              return;
  
          data[oldIdx].rename(shopName + "_" + (newIdx + 1));
          data[newIdx] = data[oldIdx];
          data[oldIdx] = null;
      }
  }
  