// ===================================================================
// Class for storing data of one Parol.
// $Id: Parol.js 3967 2009-11-16 20:47:29Z helmut $

var Bible20;
if (!Bible20) {
  Bible20 = {};
}
else if (typeof Bible20 != "object") {
  throw new Error("Bible20 already exists and is not an object");
}

if (!Bible20.Parol) {
  Bible20.Parol = {};
}
else if (typeof Bible20.Parol != "object") {
  throw new Error("Bible20.Parol already exists and is not an object");
}

Bible20.Parol.Parol = function ()
{
  try {
    this._title   = "";
    this._id      = null;
    this._canonicalid = "";
    this._updated = "";
    this._IL      = "";
    this._L       = "";
    this._SL      = "";
    this._ILPairsOk = 1;
    this._LPairsOk = 1;
  }
  catch (e) {
    alert("Parol: " + e);
  }
}

Bible20.Parol.Parol.NULL = new Bible20.Parol.Parol();

// class method: computeCanonicalID

Bible20.Parol.Parol.computeCanonicalID = function(id)
{
  var sep = "___";
  var pos = id.indexOf(sep);
  return pos >= 0 ? id.substr(pos + sep.length) : id;
}

// --- formatting helper

Bible20.Parol.Parol.prototype =
{
  _wrap: function(value, start, end)
  {
    // value as from file may contain "<em>...</em>"
    return value.replace(/<em>/g, start).replace(/<\/em>/g, end);
  },

  // --- getter ---

  getTitle: function()
  {
    return this._title;
  },

  getID: function()
  {
    return this._id;
  },

  getCanonicalID: function()
  {
    return this._canonicalid;
  },

  getUpdated: function()
  {
    return this._updated;
  },

  hasIL: function()
  {
    return !!this._IL;
  },

  getIL: function()
  {
    return this._wrap(this._IL, "", "");
  },

  getL: function()
  {
    return this._wrap(this._L, "", "");
  },

  getLOneLine: function()
  {
    return this._wrap(this._L, "", "").replace(/\n/g, " ");
  },

  getSL: function()
  {
    return this._SL;
  },


  // --- get rich ("there is //one// God")

  getRichIL: function()
  {
    return this._wrap(this._IL, "//", "//");
  },

  getRichL: function()
  {
    return this._wrap(this._L, "//", "//");
  },

  getRichOneLine: function()
  {
    var s = this._IL ? ("[" + this.getRichIL() + "] ") : "";
    return s + this.getRichL().replace(/\n/g, " ");
  },

  // --- get html ("there is <em>one</em> God")

  getHtmlIL: function()
  {
    return this._IL;
  },

  getHtmlL: function()
  {
    return this._L;
  },

  // --- get "//" pairs status

  getILPairsOk: function()
  {
    return this._ILPairsOk;
  },

  getLPairsOk: function()
  {
    return this._LPairsOk;
  },



  // === modification ===

  setID: function(text)
  {
    this._id = text;
    this._canonicalid = Bible20.Parol.Parol.computeCanonicalID(text);
    return this;
  },

  setTitle: function(text)
  {
    this._title = text;
    return this;
  },

  // see ParolBoxModel!
  setUpdated: function(updated)
  {
    this._updated = updated;
    return this;
  },


  // _buildHtml:
  // interpretes "rich" text containing "//" and builds HTML text containing "<em>";
  // e.g. for text == "there is //one// God"
  //               => "there is <em>one</em> God"
  _buildHtml: function(text, status)
  {
    try {
      var nextIndex = 0;
      var result;
      var pattern = /\/\//g;
      var bStarted = false;
      var emText = "";

      var append = function(start, end)
      {
        if (end > start) {
          var fragment = text.substr(start, end - start);
          if (!bStarted) {
            emText += fragment;
          }
          else {
            emText += "<em>" + fragment + "</em>";
          }
        }
      };

      // Concept:
      //   loop over all "//" in text and process the fragment before this "//",
      //   after the loop handle fragment past the last "//".
      //   If number of "//" is odd, insert last one as text, set error indication.
      //
      // Example:
      //    012345678901234567
      //   "pre//in//post//err"
      //
      // loop #      :    1     2     3  (after loop)
      // nextIndex   :    0     5     9   15    15
      // result.index:    3     7    13    -     -
      // appends     :  pre  (in)  post   //   err
      //
      // where "(in)" means "in" put under an "<em>" element

  //TODO low 2008-09-10 HS: this algorithm allows line breaks within an <em> area - ok for now.
      while ((result = pattern.exec(text)) != null) {
        // append text before '//'
        // (top-level text for odd occurrence of '//', <em> text for even one)
        append(nextIndex, result.index);
        nextIndex = pattern.lastIndex;
        bStarted = !bStarted;
      }
      status.pairsOk = !bStarted;
      if (bStarted) {
        // append final non-paired '//' as text
        append(nextIndex - 2, nextIndex);
        bStarted = false;
      }
      // append text (entire text if no '//' contained, or text after last '//')
      append(nextIndex, text.length);
      return emText;
    }
    catch (e) {
      alert("Parol._buildHtml: " + e);
    }
  },

  setIL: function(text)
  {
    try {
      var status = { pairsOk: 1 };
      this._IL = this._buildHtml(text, status);
      this._ILPairsOk = status.pairsOk;
      return this;
    }
    catch (e) {
      alert("Parol.setIL: " + e);
    }
  },

  setL: function(text)
  {
    try {
      if (!text) text = " "; // avoid empty text
      var status = { pairsOk: 1 };
      this._L = this._buildHtml(text, status);
      this._LPairsOk = status.pairsOk;
      return this;
    }
    catch (e) {
      alert("Parol.setL: " + e);
    }
  },

  setSL: function(text)
  {
    this._SL = text;
    return this;
  }
}