function ScormInterface(cwsObj) {
  var api = null;

  this.initialize = function() {
    var initialized = false
    api = getApi();
    if (api != null)  {
      if (api.LMSInitialize('') == "true")  {
        initialized = true;
      }
      if (api.LMSGetLastError() != "0") {
        initialized = false;
      }
    }
    return initialized;
  }

  this.setUserData = function(user) {
    var error = false;
    var userId = user.getUserId();
    var firstName = api.LMSGetValue("cmi.core.student_id");
    var lastName = firstName; //api.LMSGetValue("cmi.core.student_id");
    var password = "karta";
    var sessionTime = 0;
    var courseStatus = api.LMSGetValue("cmi.core.lesson_status");
    var courseScore = api.LMSGetValue("cmi.core.score.raw");
    if (courseScore == "")  {
      courseScore = -1;
    }
    var courseLocation = api.LMSGetValue("cmi.core.lesson_location"); //not used
    var courseCredit = api.LMSGetValue("cmi.core.credit");
    var courseEntry = api.LMSGetValue("cmi.core.entry");
    var courseMasteryScore = api.LMSGetValue("cmi.student_data.mastery_score");
    if ((courseMasteryScore != "") && (courseMasteryScore != null) && (courseMasteryScore != "null")) {
      if (!isNaN(parseFloat(courseMasteryScore))) {
        user.courseMasteryScore = parseFloat(courseMasteryScore);
      }
    }
    var courseMode = api.LMSGetValue("cmi.core.lesson_mode");
    if (cwsObj.useSCORMLessonMode)  {
      if ((courseMode == "browse") || (courseMode == "review")) {
        cwsObj.browse = true;
      }
      else  {
        cwsObj.browse = false;
      }
    }
    if (courseEntry.toLowerCase() == "ab-initio") {
      if (courseMode == "browse") {
        courseStatus = "browsed";
      }
      else  {
        courseStatus = "incomplete";
      }
    }

    var objectiveChildren = api.LMSGetValue("cmi.objectives._children");
    if ((objectiveChildren != "") && (api.LMSGetLastError() == "0"))  {
      var objectiveCount = 0;
      var tmpCount = api.LMSGetValue("cmi.objectives._count");
      if (!isNaN(tmpCount)) {
        objectiveCount = parseInt(tmpCount);
        for (var i=0;i<objectiveCount;i++)  {
          var objectiveStr = "cmi.objectives." + i;
          var id = "";
          if (objectiveChildren.indexOf("id") != -1)  {
            id = api.LMSGetValue(objectiveStr + ".id");
          }
          var minScore = -1;
          var maxScore = -1;
          var score = -1;
          if (objectiveChildren.indexOf("score") != -1)  {
            var scoreChildren = api.LMSGetValue(objectiveStr + ".score._children");
            if (scoreChildren.indexOf("min") != -1)  {
              var tmpScore = api.LMSGetValue(objectiveStr + ".score.min");
              if (!isNaN(tmpScore)) {
                minScore = parseInt(tmpScore);
              }
            }
            if (scoreChildren.indexOf("max") != -1)  {
              var tmpScore = api.LMSGetValue(objectiveStr + ".score.max");
              if (!isNaN(tmpScore)) {
                maxScore = parseInt(tmpScore);
              }
            }
            if (scoreChildren.indexOf("raw") != -1)  {
              var tmpScore = api.LMSGetValue(objectiveStr + ".score.raw");
              if (!isNaN(tmpScore)) {
                score = parseInt(tmpScore);
              }
            }
          }
          var status = "";
          if (objectiveChildren.indexOf("status") != -1)  {
            status = api.LMSGetValue(objectiveStr + ".status");
          }
          var objective = new Objective(id);
          objective.minScore = minScore;
          objective.maxScore = maxScore;
          objective.score = score;
          objective.status = status;
          user.objectives.setValue(id,objective);
        }
      }
    }


    var suspendData = api.LMSGetValue("cmi.suspend_data");
    if (api.LMSGetLastError() == "0") {
      if (!user.setUserData(firstName + "," + lastName + "," + password + "," + sessionTime + "," + courseStatus + "," + courseScore + "," + courseLocation))  {
        error = true;
      }
      if (suspendData != "")  {
        var data = CwsUtil.convertStringFromHex(suspendData);
        if (!user.setKloStatus(CwsUtil.getListValue(userId+"_ks",data,"=",";")))  {
          error = true;
        }
        if (!user.setCourseSuspendData(CwsUtil.getListValue(userId+"_csd",data,"=",";")))  {
          error = true;
        }
        for (i=0;i<cwsObj.maxDisplayTypeNum;i++)  {
          if (!user.setKloLocation(CwsUtil.getListValue(userId+"_kl"+i,data,"=",";"),i))  {
            error = true;
          }
          if (!user.setKloSuspendData(CwsUtil.getListValue(userId+"_ksd"+i,data,"=",";"),i))  {
            error = true;
          }
          if (!user.setSubKloLocation(CwsUtil.getListValue(userId+"_skl"+i,data,"=",";"),i))  {
            error = true;
          }
          if (!user.setSubKloStatus(CwsUtil.getListValue(userId+"_sks"+i,data,"=",";"),i))  {
            error = true;
          }
          user.setCourseParams(CwsUtil.getListValue(userId+"_cp"+i,data,"=",";"),i);
        }
      }
    }
    else  {
      error = true;
    }
    return !error;
  }

  this.saveUserData = function(user)  {
    var userId = user.getUserId();
    var key = "";
    var value = "";

    //user data
    api.LMSSetValue("cmi.core.session_time",convertMinutesToTime(user.sessionTime) + "");
    //api.LMSCommit('');
    api.LMSSetValue("cmi.core.lesson_status",user.courseStatus);
    //api.LMSCommit('');
    //if ((user.courseScore > -1) && ((user.courseStatus == "passed") || (user.courseStatus == "failed")))  {
    if (user.courseScore > -1)  {
      api.LMSSetValue("cmi.core.score.raw",user.courseScore + "");
      //api.LMSCommit('');
    }
    api.LMSSetValue("cmi.core.lesson_location",user.kloNum + "");
    //api.LMSCommit('');

    //klo status data
    var data = "";
    key = userId + "_ks";
    value = user.getKloStatusList();
    data += key + "=" + value + ";";

    //course suspend data
    key = userId + "_csd";
    value = user.getCourseSuspendDataList();
    data += key + "=" + value + ";";

    //klo location, sub klo status and location data
    for (i=0;i<cwsObj.maxDisplayTypeNum;i++)  {
      key = userId + "_kl" + i;
      value = user.getKloLocationList(i);
      data += key + "=" + value + ";";
      key = userId + "_ksd" + i;
      value = user.getKloSuspendDataList(i);
      data += key + "=" + value + ";";
      key = userId + "_sks" + i;
      value = user.getSubKloStatusList(i);
      data += key + "=" + value + ";";
      key = userId + "_skl" + i;
      value = user.getSubKloLocationList(i);
      data += key + "=" + value + ";";
      key = userId + "_cp" + i;
      value = user.getCourseParamList(i);
      data += key + "=" + value + ";";
    }
    api.LMSSetValue("cmi.suspend_data",CwsUtil.convertStringToHex(data));

    api.LMSCommit('');
    if (api.LMSGetLastError() != "0") {
     return false;
    }
    return true;
  }

  this.clearData = function() {

  }

  this.savePerformanceData = function(user,commit) {
    var objectives = user.objectives.getValueArray();
    if (objectives.length > 0) {
      for (var i=0;i<objectives.length;i++)  {
        this.saveObjectiveData(i,objectives[i],false);
      }
    }

    var interactions = user.interactions.getValueArray();
    if (interactions.length > 0) {
      for (var i=0;i<interactions.length;i++)  {
        this.saveInteractionData(i,interactions[i],false);
      }
    }

    if (commit) {
      api.LMSCommit('');
    }
  }

  this.saveObjectiveData = function(index,objective,commit)  {
    var oChildren = api.LMSGetValue("cmi.objectives._children");
    var objectiveStr = "cmi.objectives." + index;
    if ((objective.id != "") && (oChildren.indexOf("id") != -1)) {
      api.LMSSetValue(objectiveStr + ".id",escape(objective.id));
    }
    if (oChildren.indexOf("score") != -1)  {
      var sChildren = api.LMSGetValue(objectiveStr + ".score._children");
      if ((objective.minScore > -1) && (sChildren.indexOf("min") != -1)) {
        api.LMSSetValue(objectiveStr + ".score.min",objective.minScore);
      }
      if ((objective.maxScore > -1) && (sChildren.indexOf("max") != -1)) {
        api.LMSSetValue(objectiveStr + ".score.max",objective.maxScore);
      }
      if ((objective.score > -1) && (sChildren.indexOf("raw") != -1)) {
        api.LMSSetValue(objectiveStr + ".score.raw",objective.score);
      }
    }
    if ((objective.status != "") && (oChildren.indexOf("status") != -1)) {
      api.LMSSetValue(objectiveStr + ".status",objective.status);
    }
    if (commit) {
      api.LMSCommit('');
    }
  }

  this.saveInteractionData = function(index,interaction,commit)  {
    var iChildren = api.LMSGetValue("cmi.interactions._children");
    var iStr = "cmi.interactions." + index;
    if ((interaction.id != "") && (iChildren.indexOf("id") != -1))  {
      api.LMSSetValue(iStr + ".id",escape(interaction.id));
    }
    if (iChildren.indexOf("objectives") != -1)  {
      for (var o=0;o<interaction.objectiveIds.length;o++)  {
        var oStr = iStr + ".objectives." + o;
        var objId = interaction.objectiveIds[o];
        if (objId != "")  {
          api.LMSSetValue(oStr + ".id",escape(objId));
        }
      }
    }
    if ((interaction.time != "") && (iChildren.indexOf("time") != -1))  {
      api.LMSSetValue(iStr + ".time",interaction.time);
    }
    if ((interaction.type != "") && (iChildren.indexOf("type") != -1))  {
      api.LMSSetValue(iStr + ".type",interaction.type);
    }
    if (iChildren.indexOf("correct_responses") != -1)  {
      for (var c=0;c<interaction.correctResponses.length;c++)  {
        var cStr = iStr + ".correct_responses." + c;
        var pattern = interaction.correctResponses[c];
        if (pattern != "")  {
          api.LMSSetValue(cStr + ".pattern",pattern);
        }
      }
    }
    if ((interaction.weighting != "") && (iChildren.indexOf("weighting") != -1))  {
      api.LMSSetValue(iStr + ".weighting",interaction.weighting);
    }
    if ((interaction.studentResponse != "") && (iChildren.indexOf("student_response") != -1))  {
      api.LMSSetValue(iStr + ".student_response",interaction.studentResponse);
    }
    if ((interaction.result != "") && (iChildren.indexOf("result") != -1))  {
      api.LMSSetValue(iStr + ".result",interaction.result);
    }
    if ((interaction.latency != "") && (iChildren.indexOf("latency") != -1))  {
      api.LMSSetValue(iStr + ".latency",interaction.latency);
    }
    if (commit) {
      api.LMSCommit('');
    }
  }

  this.setScormValue = function(key,value)  {
    api.LMSSetValue(key,value);
  }

  this.getScormValue = function(key)  {
    return api.LMSGetValue(key);
  }

  this.finished = function()  {
    api.LMSSetValue('cmi.core.exit','');
    api.LMSCommit('');
    api.LMSFinish('');
  }

  function convertMinutesToTime(minutes)  {
    var hours = 0;
    if (minutes > 59) {
      var hours = Math.floor(minutes/60);
      minutes -= (hours*60);
      if (hours > 9999) {
        hours = 9999;
      }
    }
    return CwsUtil.padZeros(hours,2) + ":" + CwsUtil.padZeros(minutes,2) + ":00";
  }

  function convertTimeToMinutes(time) {
    var hours = CwsUtil.getListValueByIndex(time,0,":");
    var minutes = CwsUtil.getListValueByIndex(time,1,":");
    return parseInt(minutes,10) + (parseInt(hours) * 60);
  }

  function getApi() {
    if (cwsObj.getScormApiFromTop)  {
      return getApiFromTop();
    }
    else  {
      return getApiStandard();
    }
  }

  function findApiStandard(win) {
    var findApiTries = 0;
    while ((win.API == null) && (win.parent != null) && (win.parent != win))
    {
      findApiTries++;
      if (findApiTries > 7)
      {
        return null;
      }
      win = win.parent;
    }
    return win.API;
  }

  function getApiStandard() {
    var api = findApiStandard(window);
    if ((api == null) && (window.opener != null) && (typeof(window.opener) != "undefined"))
    {
      api = findApiStandard(window.opener);
    }
    if (api != null)  {
      return new ScormApiWrapper(api);
    }
    else  {
      return null;
    }
  }

  function findApiFromTop(win) {
    if (win.API != null) {
      return win.API;
    }
    if (win.document.API != null) {
      return win.document.API;
    }
    if (win.length > 0) {
      for (var i=0;i<win.length;i++)  {
         var api = findApiFromTop(win.frames[i]);
         if (api != null)  {
            return api;
         }
      }
    }
    return null;
  }

  function getApiFromTop() {
    var api = findApiFromTop(this.top);
    if (api == null)  {
      if (typeof(this.top.opener) != "undefined") {
        if (this.top.opener != null) {
          api = findApiFromTop(this.top.opener.top);
        }
      }
    }
    if (api != null)  {
      return new ScormApiWrapper(api);
    }
    else  {
      return null;
    }
  }
}

function ScormApiWrapper(api) {
  this.LMSInitialize = function(p) {
    return api.LMSInitialize(p)+"";
  }
  this.LMSGetValue = function(p) {
    return api.LMSGetValue(p)+"";
  }
  this.LMSSetValue = function(e, v) {
    return api.LMSSetValue(e,v)+"";
  }
  this.LMSCommit = function(p) {
    return api.LMSCommit(p)+"";
  }
  this.LMSFinish = function(p) {
    return api.LMSFinish(p)+"";
  }
  this.LMSGetLastError = function() {
    var error = api.LMSGetLastError() + "";
    if (error == "")  {
      error = "0";
    }
    return error;
  }
  this.LMSGetErrorString = function(ec) {
    return api.LMSGetErrorString(ec)+"";
  }
  this.LMSGetDiagnostic = function(ec) {
    return api.LMSGetDiagnostic(ec)+"";
  }
}

