/*
 * DBBrwoseMod.prg
 * Control TDBBrowse moderno
 *
 * Copyright 2020 Ignacio Ortiz de Zúñiga
 * Copyright 2020 https://Xailer.com
 * All rights reserved
 *
 */

#include "Xailer.ch"
#include "Dataset.ch"
#include "error.ch"

//------------------------------------------------------------------------------

CLASS XDbBrowseMod FROM TBrowseMod

PUBLISHED:
   PROPERTY aCols          INIT {} ;
                           AS TDbBrwColMod[] ;
                           EDITOR PE_StringList
   PROPERTY oDataSet       AS TDataSet ;
                           WRITE METHOD SetDataSet ;
                           EDITOR PE_Component
PUBLIC:
   PROPERTY aDisplayCols   AS TDbBrwColMod[]
   PROPERTY oEditCol       AS TDbBrwColMod

   METHOD Create( oParent )
   METHOD Free()
   METHOD SetDataSet( oDataSet )
   METHOD Refresh( lValue )
   METHOD DataCol( cDataField )

PROTECTED:
   DATA   nOldPos             INIT 0
   DATA   lOnFilter           INIT .F.
   METHOD Insert()            VIRTUAL
   EVENT  OnInsert( oSender )

   PROPERTY lAllowInsert      INIT .F.

RESERVED:
   DATA   cOrgFilter

   METHOD RunEdit( oCol, nKey )
   METHOD RunEditUndo()
   METHOD RunSave()
   METHOD RunAppend()
   METHOD RunDelete()
   METHOD AdjustColWidth( oCol, lWithImage, lCalc )
   METHOD RunSort( oCol, nSort )
   METHOD RunFilter()

   METHOD DSNavigate( nOp, Param1, Param2 )
   METHOD DrawLine() INLINE ::Refresh()
   METHOD DataValue( nIndex, lArray, nCol )
   METHOD UpdateIndex( nIndex )
   METHOD LocateIndex( nRecno, lRefresh )

   METHOD SetRecnoPos( nItem )
   METHOD RestoreRecnoPos()

END CLASS

//------------------------------------------------------------------------------

METHOD Create( oParent ) CLASS XDbBrowseMod

   ::Super:Create( oParent )

   IF ::oDataSet != NIL
      ::SetDataSet( ::FoDataSet )
   ENDIF

RETURN Self

//------------------------------------------------------------------------------

METHOD Free() CLASS xDbBrowseMod

   IF ::oDataSet != Nil
      IF ::oDataSet:lOpen .AND. ::oDataset:oParent == NIL  // Es dataset autónomo
         IF ! Empty( ::cOrgFilter )
            ::oDataSet:Filter( ::cOrgFilter, .F., .F. )
            ::cOrgFilter := NIL
         ELSEIF !Empty( ::oDataSet:Filter() )
            ::oDataSet:Filter( "", .F., .F. )
         ENDIF
      ENDIF
      ::SetDataSet( Nil )
   ENDIF

RETURN ::Super:Free()

//------------------------------------------------------------------------------

METHOD DataValue( nIndex, lArray, nCol ) CLASS xDbBrowseMod

   LOCAL aData
   LOCAL cText

   IF ::oDataSet == nil .OR. nIndex <= 0 .OR. !::oDataSet:lOpen
      RETURN IIF( Empty( lArray ), "", {} )
   ENDIF

   aData := ::oDataset:GetRow( nIndex, .F. )

   IF !Empty( lArray )
      RETURN aData
   ENDIF

   IF Empty( aData )
      cText := ""
   ELSEIF Empty( nCol ) .AND. ::lShowCellSelected .AND. ::oSelectCol != NIL
      WITH OBJECT ::oSelectCol
         IF !Empty( :cPicture )
            cText := Transform( aData[ :nDataCol ], :cPicture )
         ELSE
            cText := ToString( aData[ :nDataCol ] )
         ENDIF
      END WITH
   ELSE
      DEFAULT nCol TO 1
      WITH OBJECT ::aCols[ nCol ]
         IF :nDatacol > 0
            cText := ToString( aData[ :nDataCol ] )
         ELSE
            cText := ""
         ENDIF
      END WITH
   ENDIF

RETURN cText

//------------------------------------------------------------------------------

METHOD RunEdit() CLASS XDbBrowseMod

RETURN ::oDataSet:FastEdit()

//------------------------------------------------------------------------------

METHOD RunEditUndo() CLASS XDbBrowseMod

RETURN ::oDataSet:Cancel()

//------------------------------------------------------------------------------

METHOD RunAppend() CLASS XDbBrowseMod

RETURN ::oDataSet:FastAddNew()

//------------------------------------------------------------------------------

METHOD RunDelete() CLASS XDbBrowseMod

   LOCAL nRec

   WITH OBJECT ::oDataSet
      nRec := :Recno()
      IF :Delete( .T. )
         RETURN .T.
      ENDIF
   END WITH

RETURN .F.

//------------------------------------------------------------------------------

METHOD RunSave() CLASS XDbBrowseMod

   LOCAL oCol

   IF ::oEditCol == NIL
      FOR EACH oCol IN ::aCols
         oCol:SetValue( oCol:oGridEdit:Value() )
      NEXT
   ELSE
      WITH OBJECT ::oEditCol
         :SetValue( :oGridEdit:Value() )
      END WITH
   ENDIF

   ::oDataSet:Update( .T. )
   ::Refresh()

RETURN .T.

//------------------------------------------------------------------------------

METHOD SetDataSet( oDataSet ) CLASS xDbBrowseMod

   LOCAL oCol AS CLASS TDbBrwColMod
   LOCAL oField AS CLASS TDataField
   LOCAL aWork

   IF !::lCreated
      ::FoDataSet := oDataSet
      //::FoDataSet:oDBBrowse := Self
      RETURN oDataSet
   ENDIF

   IF ::FoDataSet != Nil
      ::FoDataSet:oDbBrowse := Nil
      FOR EACH oCol IN ::aCols
         oCol:ResetDataField()
      NEXT
   ENDIF

   ::FoDataSet := oDataSet

   IF oDataSet != Nil
      ::FoDataSet:oDBBrowse := Self
      IF oDataSet:lOpen
         ::BeginPaint()
         FOR EACH oCol IN ::aCols
            WITH OBJECT oCol
               IF Valtype( :oDataField ) == "C"
                  :SetFieldObject( :oDataField, .F. )
               ENDIF
            END WITH
         NEXT
         IF Len( ::aCols ) == 0
            FOR EACH oField IN oDataSet:aFields
               WITH OBJECT oField
                  oCol := TDBBrwColMod():New( Self )
                  oCol:cHeader    := :cDisplayName
                  oCol:nAlignment := IIf( :cType == "N", taRIGHT, taLEFT )
                  oCol:nDataAlignment := oCol:nAlignment
                  oCol:SetFieldObject( oField, .F. )
                  oCol:Create()
               END WITH
            NEXT
         ENDIF
         IF ::oDataGrid != NIL
            aWork := Array( ::oDataSet:RecCount() )
            AEval( aWork, {| v, e | aWork[ e ] := e } )
            ::oDataGrid:SetItems( aWork, .f. )
         ENDIF

         ::EndPaint()
         ::SetShowCellSelected()
         ::cOrgFilter := ::oDataSet:Filter()
         ::nIndex     := ::oDataSet:Recno()
      ENDIF
   ENDIF

RETURN ::FoDataset

//------------------------------------------------------------------------------

METHOD DSNavigate( nOp, Param1, Param2 ) CLASS XDbBrowseMod

   LOCAL nRecno

   SWITCH nOp
   CASE XA_DSNAV_GOTOP
   CASE XA_DSNAV_GOBOTTOM
   CASE XA_DSNAV_GOTO
   CASE XA_DSNAV_KEYGOTO
   CASE XA_DSNAV_SKIP
   CASE XA_DSNAV_SEEK
   CASE XA_DSNAV_LOCATE
   CASE XA_DSNAV_CONTINUE
      ::LocateIndex()
      EXIT
   CASE XA_DSNAV_REFRESH
   CASE XA_DSNAV_REFRESHCURRENT
   CASE XA_DSNAV_EDIT
      ::Refresh(.F.)
      EXIT
   CASE XA_DSNAV_OTHER
      nRecno := ::oDataSet:Recno()
      ::RunFilter()
      ::LocateIndex( nRecno )
      EXIT
   CASE XA_DSNAV_ADDNEW
   CASE XA_DSNAV_DELETE
   CASE XA_DSNAV_FULLREFRESH
      ::RunFilter()
      EXIT
   END SWITCH

RETURN Nil

//------------------------------------------------------------------------------

METHOD UpdateIndex( nIndex ) CLASS XDbBrowseMod

   ::Super:UpdateIndex( nIndex )

   IF ::oDataSet != NIL .AND. !Empty( nIndex )
      WITH OBJECT ::oDataSet
         IF nIndex <= Len( ::oDataGrid:aWork )
            :GoTo( ::oDataGrid:aWork[ nIndex ], .f. )
         ENDIF
         AEval( :aLinkedControls, {|v| v:Refresh( ) } )
      END WITH
   ENDIF

RETURN nil

//------------------------------------------------------------------------------

METHOD LocateIndex( nRecno, lRefresh ) CLASS XDbBrowseMod

   LOCAL nIndex

   DEFAULT nRecno TO ::oDataSet:Recno(), lRefresh TO .T.

   nIndex := AScan( ::oDataGrid:aWork, {|v| v == nRecno } )

   IF nIndex > 0 .AND. lRefresh
      ::oDataGrid:SetIndex( nIndex )
   ENDIF

RETURN nIndex

//------------------------------------------------------------------------------

METHOD AdjustColWidth( oCol, lWithImage, lCalc ) CLASS xDbBrowseMod

   LOCAL oItem, oFont
   LOCAL xValue
   LOCAL cPicture
   LOCAL nDataCol, nWidth, nMax

   oFont := ::oFont

   DEFAULT lWithImage TO .T. , lCalc TO .F.

   IF oCol:IsKindOf( "TBrwColMod" )
      oItem := oCol:oHeaderCol
   ELSE
      oItem := oCol
   ENDIF

   WITH OBJECT oItem:oGridCol
      nDataCol := :nDataCol
      cPicture := :cPicture
      nDataCol := :nDataCol
   END WITH

   nMax := MulDiv( oFont:GetTextWidth( oItem:cHeader, Self ), 120, 100 )

   WITH OBJECT ::oDataSet
      :SaveState( .T. )
      :GoTop()
      DO WHILE !:Eof()
         xValue := :FieldGet( nDataCol, .T. )
         IF !Empty( xValue )
            nWidth := oFont:GetTextWidth( Transform( xValue, cPicture ), Self )
            IF nWidth > nMax
               nMax := nWidth
            ENDIF
         ENDIF
         :Skip()
      ENDDO
      :RestoreState( .T. )
   END WITH

   nMax += ( ::oDataGrid:nMargin * 2 )
   IF lWithImage .AND. oItem:oGridCol:lDataImageUsed
      nMax += ::oImageList:nWidth
   ENDIF
   nMax := Min( nMax, ::nClientWidth - 50 )

   IF !lCalc
      oItem:oGridCol:nWidth := nMax
   ENDIF

RETURN nMax

//------------------------------------------------------------------------------

METHOD RunSort( oCol, nSort ) CLASS XDbBrowseMod

//   LOCAL aSel
   LOCAL cExp
   LOCAL lRet

   IF ::oDataSet == Nil .OR. oCol == NIL .OR. Valtype( oCol:oDataField ) != "O" ;
      .OR. !::oDataset:lOpen
      RETURN .F.
   ENDIF

   DEFAULT nSort TO oCol:oHeaderCol:nSort

//   aSel := ::GetSelected() IOZ(11/01/24): Ya se hace en metodo anterior Sort()
   cExp := oCol:oDataField:cName

   IF nSort == hsDESCENDING
      cExp  += " DESC"
   ENDIF

   lRet := ::oDataSet:Sort( cExp, .F., .F. )

//   ::SetSelected( aSel )

RETURN lRet

//------------------------------------------------------------------------------

METHOD RunFilter() CLASS XDbBrowseMod

   LOCAL aWork
   LOCAL cNewFilter, cOldFilter

   IF ::oDataSet == Nil .OR. !::oDataSet:lOpen .OR. ::lOnFilter
      RETURN .F.
   ENDIF

   ::aSelected := {} // With filters we clean rows selected
   ::lOnFilter := .T.

   IF ::lFilterBar
      cNewFilter := ::oDataset:GetFilterExp( Self )
   ENDIF

   IF Empty( cNewFilter )
      ::oDataSet:Filter( ::cOrgFilter, .T.,.F., .T. )
   ELSE
      TRY
      ::oDataSet:Filter( cNewFilter, .T., .F., .T. )
      CATCH
         ::oDataSet:Filter( ::cOrgFilter, .T., .F., .T. )
      END
   ENDIF

   aWork := Array( ::oDataSet:RecCount() )

   AEval( aWork, {| v, e | aWork[ e ] := e } )
   ::oDataGrid:aItems := aWork

   IF ::IsSorted()
      ::Resort( .f. )
   ENDIF

   AEval( ::oDataset:aLinkedControls, {|v| v:Refresh() } )

   ::lOnFilter := .F.

RETURN .T.

//------------------------------------------------------------------------------

METHOD SetRecnoPos( nItem ) CLASS XDbBrowseMod

   IF ::oDataSet != nil
      WITH OBJECT ::oDataSet
         IF Empty( ::nOldPos )
            ::nOldPos := :Recno()
         ENDIF
         :GoTo( nItem, .F. )
      END WITH
   ELSE
      ::nOldPos := 0
   ENDIF

RETURN nil

//------------------------------------------------------------------------------

METHOD RestoreRecnoPos() CLASS XDbBrowseMod

   IF ::oDataSet != nil .and. ::nOldPos > 0
      ::oDataSet:GoTo( ::nOldpos, .F. )
      ::nOldPos := 0
   ENDIF

RETURN nil

//------------------------------------------------------------------------------

METHOD Refresh( lValue ) CLASS XDbBrowseMod

   DEFAULT lValue TO .F.

   IF lValue .AND. ::oDataSet != NIL .AND. ::oDataSet:lOpen
      ::RunFilter()
   ENDIF

RETURN ::Super:Refresh( lValue )

//------------------------------------------------------------------------------

METHOD DataCol( cDataField ) CLASS XDbBrowseMod

   LOCAL nAt

   cDataField := Upper( cDataField )

   nAt := AScan( ::aCols, {|v| v:cDataField == cDataField } )

   IF nAt > 0
      RETURN ::aCols[ nAt ]
   ENDIF

RETURN NIL

//------------------------------------------------------------------------------