﻿    //RecordView class
    // id - required, unique in page id of the view
    // rec - required, data record instance
    // gui - GUI library, if null then default script "wv.gui.js" must be included
    // manualViews - if true then view controls will not be auto-constructed
    published.RecordView = function(id, rec, gui, manualViews)
    {
        if (WAVE.strEmpty(id)) throw "RecordView.ctor(id: must specify)";
        if (!WAVE.isObject(rec)) throw "RecordView.ctor(rec: must be Record)";
        if (!WAVE.isObject(gui)) gui = WAVE.RecordModel.GUI;//dflt GUI lib
        var fID = id;
        var fRecord = rec;
        var fGUI = gui;
        var recview = this;
        var fViews = [];
        var fRootElement = null;


        //Returns record view instance ID used for FieldView bindings
        this.ID = function(){ return fID;};

        //Returns bound record
        this.record = function(){ return fRecord;};

        //Returns GUI backend that renders controls for the view
        this.gui = function(){ return fGUI;};

        //Returns root element that this record view is building controls under
        this.rootElement = function() { return fRootElement;};


        //Unbinds and deletes all views
        this.destroyViews = function(){
          while(fViews.length>0) fViews[0].destroy();
        };

        var divHiddenFields = null;

        //Builds and binds view controls to record fields from declarative page markup: <...dava-wv-rid='r1'...> <div data-wv-fld='LastName' ... />
        this.buildViews = function(){
          fRootElement = null;
          this.destroyViews();

          var allForms = document.getElementsByTagName("form");
          for (var i=0, imax=allForms.length; i < imax; i++) {
            var frm = allForms[i];

            var irid = frm.getAttribute(published.DATA_RECVIEW_ID_ATTR);
            if (WAVE.strSame(fID, irid))
            {
                fRootElement = frm;
                break;
            }
          }

          if (fRootElement===null){
              var allDIVS = document.getElementsByTagName("div");
              for (var j=0, jmax=allDIVS.length; j < jmax; j++) {
                var div = allDIVS[j];
                var jrid = div.getAttribute(published.DATA_RECVIEW_ID_ATTR);
                if (WAVE.strSame(fID, jrid))
                {
                    fRootElement = div;
                    break;
                }
              }
          }

          if (fRootElement===null) throw "No 'form' or 'div' element could be found with record binding '"+published.DATA_RECVIEW_ID_ATTR+"' annotation";

          //find DIVs for views, need copy as DOM indexes will shift as views get added
          var allViewDIVS = WAVE.arrayShallowCopy( fRootElement.getElementsByTagName("div") );

          for (var k=0, kmax=allViewDIVS.length; k < kmax; k++) {
                var kdiv = allViewDIVS[k];
                var fname = kdiv.getAttribute(published.DATA_FIELD_NAME_ATTR);
                if (!WAVE.strEmpty(fname))
                {
                    if (fname==="#")//whole-record-level binding
                    {
                      new this.FieldView(kdiv, null);//bind to whole record
                    }
                    else
                    {
                      var fld = fRecord.fieldByName(fname);
                      if (fld!==null) new this.FieldView(kdiv, fld);
                    }
                }
          }


          if (!WAVE.strEmpty(fRecord.formMode()) || !WAVE.strEmpty(fRecord.csrfToken()) || !WAVE.strEmpty(fRecord.roundtrip()))
          {
             if (divHiddenFields===null)
             {
                divHiddenFields = document.createElement("div");
                divHiddenFields.style.display = "none";
                fRootElement.appendChild(divHiddenFields);
             }

             var content = "";

             if (!WAVE.strEmpty(fRecord.formMode()))
                content += "<input type='hidden' name='__FormMode' value='"+fRecord.formMode()+"'>";

             if (!WAVE.strEmpty(fRecord.csrfToken()))
                content +=  "<input type='hidden' name='__CSRFToken' value='"+fRecord.csrfToken()+"'>";

             if (!WAVE.strEmpty(fRecord.roundtrip()))
                content +=  "<input type='hidden' name='__Roundtrip' value='"+fRecord.roundtrip()+"'>";

             divHiddenFields.innerHTML = content;
          }

        };

        //Returns copy of view list
        this.views = function() {
           return WAVE.arrayShallowCopy(fViews);
        };


        //Individual field view class, if fld==null binds to whole record context
        this.FieldView = function(div, fld)
        {
            if (!div) throw "FieldView.ctor(div: element must be passed)";
            if (fld!==null && !WAVE.isObject(fld)) throw "FieldView.ctor(fld: must be Record.Field)";
            var fField = fld;
            var fieldview = this;
            var fDIV = div;
            fViews.push( fieldview );//register with parent

            if (fField!==null)
              fField.eventSinkBind(this);
            else
              fRecord.eventSinkBind(this);


            var recPropertyName = "";

            if (fld!==null)
            {
              recPropertyName = "fld"+fld.name();
              recview[recPropertyName] = fieldview;//make recview.fldLastName shortcut
            }

            //Invoked by control changes
            this.eventNotify = function(evtName, sender, phase){
              if (!WAVE.strOneOf(evtName,[WAVE.RecordModel.EVT_DATA_CHANGE,
                                          WAVE.RecordModel.EVT_INTERACTION_CHANGE,
                                          WAVE.RecordModel.EVT_VALIDATION_DEFINITION_CHANGE,
                                          WAVE.RecordModel.EVT_VALIDATED])) return;

              if (typeof(phase)===tUNDEFINED) phase = "";
              if (phase === WAVE.RecordModel.EVT_PHASE_BEFORE) return;

              if (fField!==null)
                fDIV.style.visibility = fField.visible() ? "visible" : "hidden";

              fGUI.eventNotifyFieldView( fieldview, evtName, sender, phase);
            };

            //Unbinds the view and deletes all internal markup
            this.destroy = function(){
                fDIV.innerHTML = "";//destroy control content
                if (fField!==null)
                  fField.eventSinkUnbind(this);
                else
                  fRecord.eventSinkUnbind(this);

                WAVE.arrayDelete(fViews, this);

                if (fField!==null)
                  delete recview[recPropertyName];
            };

            this.record = function(){ return fRecord; };
            this.recView = function() { return recview;};
            this.field = function(){ return fField; };


            //Returns root element in which the "visual control" gets built
            this.DIV = function(){ return fDIV; };

            //Gets control type specified on this view or infersfrom field
            this.getOrInferControlType = function(){
               var ctp = fDIV.getAttribute(published.DATA_CTL_TP_ATTR);
               if (!WAVE.strEmpty(ctp)) return ctp;
               return fField===null ? WAVE.RecordModel.CTL_TP_ERROR_REC : fField.getOrInferControlType();
            };

            if (fField===null || fField.visible())
                fGUI.buildFieldViewAnew( this );
        };//FieldView


        if (!manualViews) this.buildViews();
    };//RecordView