content: /*
   * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package my.project.moviesbox.custom;
  
  import static master.flame.danmaku.danmaku.model.IDanmakus.ST_BY_TIME;
  
  import android.graphics.Color;
  import android.text.TextUtils;
  
  import org.json.JSONArray;
  import org.json.JSONException;
  import org.xml.sax.Attributes;
  import org.xml.sax.InputSource;
  import org.xml.sax.SAXException;
  import org.xml.sax.XMLReader;
  import org.xml.sax.helpers.DefaultHandler;
  import org.xml.sax.helpers.XMLReaderFactory;
  
  import java.io.BufferedReader;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.InputStreamReader;
  import java.io.StringReader;
  import java.nio.charset.StandardCharsets;
  import java.util.Locale;
  import java.util.regex.Matcher;
  import java.util.regex.Pattern;
  import java.util.stream.Collectors;
  
  import master.flame.danmaku.danmaku.model.AlphaValue;
  import master.flame.danmaku.danmaku.model.BaseDanmaku;
  import master.flame.danmaku.danmaku.model.Duration;
  import master.flame.danmaku.danmaku.model.IDisplayer;
  import master.flame.danmaku.danmaku.model.SpecialDanmaku;
  import master.flame.danmaku.danmaku.model.android.DanmakuFactory;
  import master.flame.danmaku.danmaku.model.android.Danmakus;
  import master.flame.danmaku.danmaku.parser.BaseDanmakuParser;
  import master.flame.danmaku.danmaku.parser.android.AndroidFileSource;
  import master.flame.danmaku.danmaku.util.DanmakuUtils;
  import my.project.moviesbox.utils.Utils;
  
  /**
    * @包名: my.project.moviesbox.custom
    * @类名: XmlDanmukuParser
    * @描述: XML格式弹幕解析，根据官方DEMO修改解析
    * @作者: Li Z
    * @日期: 2024/1/22 20:07
    * @版本: 1.0
   */
  public class DanmukuXmlParser extends BaseDanmakuParser {
  
      static {
          System.setProperty("org.xml.sax.driver", "org.xmlpull.v1.sax2.Driver");
      }
  
      protected float mDispScaleX;
      protected float mDispScaleY;
  
      @Override
      public Danmakus parse() {
  
          if (mDataSource != null) {
              AndroidFileSource source = (AndroidFileSource) mDataSource;
              try {
                  XMLReader xmlReader = XMLReaderFactory.createXMLReader();
                  XmlContentHandler contentHandler = new XmlContentHandler();
                  xmlReader.setContentHandler(contentHandler);
  //                xmlReader.parse(new InputSource(source.data()));
                  InputStream inputStream = source.data();
                  String rawXml = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))
                          .lines().collect(Collectors.joining("\n"));
                  String cleanedXml = fixIllegalXmlText(rawXml);
                  InputSource inputSource = new InputSource(new StringReader(cleanedXml));
                  xmlReader.parse(inputSource);
                  return contentHandler.getResult();
              } catch (SAXException e) {
                  e.printStackTrace();
              } catch (IOException e) {
                  e.printStackTrace();
              }
  
          }
  
          return null;
      }
  
      /**
       * 处理异常XML
       * @param xml
       * @return
       */
      public static String fixIllegalXmlText(String xml) {
          // 先处理 < /d> 标签空格问题
          xml = xml.replaceAll("<\\s+/\\s*", "</");
  
          // 匹配 <d ...>内容</d> 并替换中间的非法 < 和 &
          Pattern pattern = Pattern.compile("(<d\\s+[^>]+>)(.*?)(</d>)", Pattern.DOTALL);
          Matcher matcher = pattern.matcher(xml);
          StringBuffer sb = new StringBuffer();
          while (matcher.find()) {
              String openingTag = matcher.group(1);
              String content = matcher.group(2);
              String closingTag = matcher.group(3);
  
              // 转义非法字符
              content = content.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;");
              matcher.appendReplacement(sb, Matcher.quoteReplacement(openingTag + content + closingTag));
          }
          matcher.appendTail(sb);
  
          // 补全根标签
          if (!sb.toString().trim().endsWith("</i>")) {
              sb.append("</i>");
          }
  
          return sb.toString();
      }
  
      public class XmlContentHandler extends DefaultHandler {
  
          private static final String TRUE_STRING = "true";
  
          public Danmakus result;
  
          public BaseDanmaku item = null;
  
          public boolean completed = false;
  
          public int index = 0;
  
          public Danmakus getResult() {
              return result;
          }
  
          @Override
          public void startDocument() throws SAXException {
              result = new Danmakus(ST_BY_TIME, false, mContext.getBaseComparator());
          }
  
          @Override
          public void endDocument() throws SAXException {
              completed = true;
          }
  
          @Override
          public void startElement(String uri, String localName, String qName, Attributes attributes)
                  throws SAXException {
              String tagName = localName.length() != 0 ? localName : qName;
              tagName = tagName.toLowerCase(Locale.getDefault()).trim();
              if (tagName.equals("d")) {
                  // <d p="23.826000213623,1,25,16777215,1422201084,0,057075e9,757076900">我从未见过如此厚颜无耻之猴</d>
                  // 0:时间(弹幕出现时间)
                  // 1:类型(1从右至左滚动弹幕|6从左至右滚动弹幕|5顶端固定弹幕|4底端固定弹幕|7高级弹幕|8脚本弹幕)
                  // 2:字号
                  // 3:颜色
                  // 4:时间戳 ?
                  // 5:弹幕池id
                  // 6:用户hash
                  // 7:弹幕id
                  String pValue = attributes.getValue("p");
                  // parse p value to danmaku
                  String[] values = pValue.split(",");
                  if (values.length > 0) {
                      long time = (long) (parseFloat(values[0]) * 1000); // 出现时间
                      int type = parseInteger(values[1]); // 弹幕类型
  //                    float textSize = parseFloat(values[2]); // 字体大小
                      int color = (int) ((0x00000000ff000000 | parseLong(values[3])) & 0x00000000ffffffff); // 颜色
                      // int poolType = parseInteger(values[5]); // 弹幕池类型（忽略
                      item = mContext.mDanmakuFactory.createDanmaku(type, mContext);
                      if (item != null) {
                          item.setTime(time + 1200);
                          item.textColor = color;
                          item.textShadowColor = color <= Color.BLACK ? Color.WHITE : Color.BLACK;
                          item.flags = mContext.mGlobalFlagValues;
                          item.setTimer(mTimer);
                          item.underlineColor = Color.TRANSPARENT;
                          item.borderColor = Color.TRANSPARENT;
                          item.priority = 0; //  一旦设置 >0的属性，那个防止重叠和最大行数就会失效
                          item.textSize = (Utils.isPad() ? 26f : 22f) * (mDispDensity - 0.6f);
                      }
                  }
              }
          }
  
          @Override
          public void endElement(String uri, String localName, String qName) throws SAXException {
              if (item != null && item.text != null) {
                  if (item.duration != null) {
                      String tagName = localName.length() != 0 ? localName : qName;
                      if (tagName.equalsIgnoreCase("d")) {
                          item.setTimer(mTimer);
                          item.flags = mContext.mGlobalFlagValues;
                          Object lock = result.obtainSynchronizer();
                          synchronized (lock) {
                              result.addItem(item);
                          }
                      }
                  }
                  item = null;
              }
          }
  
          @Override
          public void characters(char[] ch, int start, int length) {
              if (item != null) {
                  DanmakuUtils.fillText(item, decodeXmlString(new String(ch, start, length)));
                  item.index = index++;
  
                  // initial specail danmaku data
                  String text = String.valueOf(item.text).trim();
                  if (item.getType() == BaseDanmaku.TYPE_SPECIAL && text.startsWith("[")
                          && text.endsWith("]")) {
                      //text = text.substring(1, text.length() - 1);
                      String[] textArr = null;//text.split(",", -1);
                      try {
                          JSONArray jsonArray = new JSONArray(text);
                          textArr = new String[jsonArray.length()];
                          for (int i = 0; i < textArr.length; i++) {
                              textArr[i] = jsonArray.getString(i);
                          }
                      } catch (JSONException e) {
                          e.printStackTrace();
                      }
  
                      if (textArr == null || textArr.length < 5 || TextUtils.isEmpty(textArr[4])) {
                          item = null;
                          return;
                      }
                      DanmakuUtils.fillText(item, textArr[4]);
                      float beginX = parseFloat(textArr[0]);
                      float beginY = parseFloat(textArr[1]);
                      float endX = beginX;
                      float endY = beginY;
                      String[] alphaArr = textArr[2].split("-");
                      int beginAlpha = (int) (AlphaValue.MAX * parseFloat(alphaArr[0]));
                      int endAlpha = beginAlpha;
                      if (alphaArr.length > 1) {
                          endAlpha = (int) (AlphaValue.MAX * parseFloat(alphaArr[1]));
                      }
                      long alphaDuraion = (long) (parseFloat(textArr[3]) * 1000);
                      long translationDuration = alphaDuraion;
                      long translationStartDelay = 0;
                      float rotateY = 0, rotateZ = 0;
                      if (textArr.length >= 7) {
                          rotateZ = parseFloat(textArr[5]);
                          rotateY = parseFloat(textArr[6]);
                      }
                      if (textArr.length >= 11) {
                          endX = parseFloat(textArr[7]);
                          endY = parseFloat(textArr[8]);
                          if (!"".equals(textArr[9])) {
                              translationDuration = parseInteger(textArr[9]);
                          }
                          if (!"".equals(textArr[10])) {
                              translationStartDelay = (long) (parseFloat(textArr[10]));
                          }
                      }
                      if (isPercentageNumber(textArr[0])) {
                          beginX *= DanmakuFactory.BILI_PLAYER_WIDTH;
                      }
                      if (isPercentageNumber(textArr[1])) {
                          beginY *= DanmakuFactory.BILI_PLAYER_HEIGHT;
                      }
                      if (textArr.length >= 8 && isPercentageNumber(textArr[7])) {
                          endX *= DanmakuFactory.BILI_PLAYER_WIDTH;
                      }
                      if (textArr.length >= 9 && isPercentageNumber(textArr[8])) {
                          endY *= DanmakuFactory.BILI_PLAYER_HEIGHT;
                      }
                      item.duration = new Duration(alphaDuraion);
                      item.rotationZ = rotateZ;
                      item.rotationY = rotateY;
                      mContext.mDanmakuFactory.fillTranslationData(item, beginX,
                              beginY, endX, endY, translationDuration, translationStartDelay, mDispScaleX, mDispScaleY);
                      mContext.mDanmakuFactory.fillAlphaData(item, beginAlpha, endAlpha, alphaDuraion);
  
                      if (textArr.length >= 12) {
                          // 是否有描边
                          if (!TextUtils.isEmpty(textArr[11]) && TRUE_STRING.equalsIgnoreCase(textArr[11])) {
                              item.textShadowColor = Color.TRANSPARENT;
                          }
                      }
                      if (textArr.length >= 13) {
                          //TODO 字体 textArr[12]
                      }
                      if (textArr.length >= 14) {
                          // Linear.easeIn or Quadratic.easeOut
                          ((SpecialDanmaku) item).isQuadraticEaseOut = ("0".equals(textArr[13]));
                      }
                      if (textArr.length >= 15) {
                          // 路径数据
                          if (!"".equals(textArr[14])) {
                              String motionPathString = textArr[14].substring(1);
                              if (!TextUtils.isEmpty(motionPathString)) {
                                  String[] pointStrArray = motionPathString.split("L");
                                  if (pointStrArray.length > 0) {
                                      float[][] points = new float[pointStrArray.length][2];
                                      for (int i = 0; i < pointStrArray.length; i++) {
                                          String[] pointArray = pointStrArray[i].split(",");
                                          if (pointArray.length >= 2) {
                                              points[i][0] = parseFloat(pointArray[0]);
                                              points[i][1] = parseFloat(pointArray[1]);
                                          }
                                      }
                                      mContext.mDanmakuFactory.fillLinePathData(item, points, mDispScaleX,
                                              mDispScaleY);
                                  }
                              }
                          }
                      }
                  }
  
              }
          }
  
          private String decodeXmlString(String title) {
              if (title.contains("&amp;")) {
                  title = title.replace("&amp;", "&");
              }
              if (title.contains("&quot;")) {
                  title = title.replace("&quot;", "\"");
              }
              if (title.contains("&gt;")) {
                  title = title.replace("&gt;", ">");
              }
              if (title.contains("&lt;")) {
                  title = title.replace("&lt;", "<");
              }
              return title;
          }
  
      }
  
      private boolean isPercentageNumber(String number) {
          //return number >= 0f && number <= 1f;
          return number != null && number.contains(".");
      }
  
      private float parseFloat(String floatStr) {
          try {
              return Float.parseFloat(floatStr);
          } catch (NumberFormatException e) {
              return 0.0f;
          }
      }
  
      private int parseInteger(String intStr) {
          try {
              return Integer.parseInt(intStr);
          } catch (NumberFormatException e) {
              return 0;
          }
      }
      
      private long parseLong(String longStr) {
          try {
              return Long.parseLong(longStr);
          } catch (NumberFormatException e) {
              return 0;
          }
      }
  
      @Override
      public BaseDanmakuParser setDisplayer(IDisplayer disp) {
          super.setDisplayer(disp);
          mDispScaleX = mDispWidth / DanmakuFactory.BILI_PLAYER_WIDTH;
          mDispScaleY = mDispHeight / DanmakuFactory.BILI_PLAYER_HEIGHT;
          return this;
      }
  }
  