Attribute VB_Name = "DatabaseImpl"
' ======== Модуль с константами и функциями для работы с базой данных =====================
Option Private Module
Option Explicit

Public Const HT03_DBNAME = "!Реестр НПА.xlsm"    'Имя основого файла Реестра

Public Const HT03_DOC_EXTENSION = ".docx"

Public Const HT03_DEFAULT_FIRST_LINE = 2       ' Первая строка под шапку

Public Const HT03_INVALID_ROW = -1
Public Const HT03_INVALID_GID = -1

Public Const HT03_SHT_DOCS = "Документы"
Public Const HT03_SHT_LINKS = "Связи"
Public Const HT03_SHT_STATS = "Статистика"
Public Const HT03_SHT_INFO = "Справочники"

Public Const HT03_FILE_NOT_LOADED = "!Документ не загружен"
Public Const HT03_TITLE_NOT_LOADED = "!Название не определено"

Public Const HT03_DEFAULT_WORKDIR = "Документы"

Public Const HT03_CELL_HASH = "c_Hash"
Public Const HT03_CELL_FOLDER = "c_Folder"

Public Const HT03_DUMMY_DOCUMENT_DISCLAIMER = _
  "Текст документа не загружен. " & _
  "Используйте титульную ссылку для перехода в ГАРАНТ и скачайте текст документа. " & _
  "Если текст документа отсутствует в ГАРАНТе, то его можно скопировать в этот документ, сохраняя титульную ссылку выше."
  
' Errors raised by DB
Public Enum DBErrors
  ERR_SYNC_DB = vbObjectError + 1000
  ERR_SYNC_DOC
  ERR_LINKS_MISSING
  ERR_SYNC_LINK_ID
  ERR_INVALID_LINK_NUMBER
  ERR_LINK_NO_FILE
  ERR_BOOKMARK_INVALID
  ERR_LINK_SELF
  ERR_ADD_LINK_DB_FAILED
  ERR_ADD_LINK_DOC_FAILED
End Enum

'# _D_ Номера столбцов в таблице документов БД
Public Enum LawStruct
  [_First] = 1
  
  S_D_GARANT_ID = 1     ' ID документа в базе Гаранта
  S_D_STATUS = 2        ' Статус документа (см. ниже)
  S_D_TYPE = 3          ' Тип документа
  S_D_FILENAME = 4      ' Имя файла в папке реестра
  S_D_TITLE = 5         ' Название документа
  S_D_TYPE_TXT = 6      ' Название типа
  S_D_CREATOR = 7       ' Выпускающий орган
  S_D_DATE = 8          ' Дата выпуска
  S_D_NUMBER = 9        ' Номер
  S_D_HEADER = 10       ' Титул
  S_D_ATTACH = 11       ' Вложенный документ
  S_D_TIME_SCOPED = 12  ' Срочность документа
  S_D_IS_DUMMY = 13     ' Пустой документ
  
  S_D_LINKBASE = 14     ' Номер строки с первой ссылкой документа в БД ссылок
  
  [_Last] = 14
End Enum

'# _L_ Номера столбцов в таблице ссылок БД
Public Enum LinkStruct
  [_First] = 1
  
  S_L_FROM_ID = 1     ' ID документа, содержащего ссылку
  S_L_FROM_SUB = 2    ' ID закладки в файле
  S_L_TO_ID = 3       ' ID документа, на который ведет ссылка
  S_L_TO_SUB = 4      ' ID закладки на которую ссылка
  S_L_TYPE = 5      ' Тип ссылки (см. ниже)
  S_L_STATUS = 6      ' Статус ссылки (см. ниже)
  
  [_Last] = 6
End Enum

'# _CD_ Структура кешированных данных о документах
Public Enum LawCacheStruct
  [_First] = 1
  
  S_CD_ID = 1
  S_CD_TYPE = 2
  S_CD_STATUS = 3
  S_CD_LINKBASE = 4

  [_Last] = 4
End Enum

'# _CL_ Структура кешированных данных о ссылках
Public Enum LinkCacheStruct
  [_First] = 1
  
  S_CL_DESTINATION_ID = 1
  S_CL_TYPE = 2
  S_CL_STATUS = 3

  [_Last] = 3
End Enum

'# _TL_ Структура передаваемых данных о ссылках
Public Enum LinkTransferStruct
  [_First] = 1
  
  S_TL_DESTINATION_ID = 1
  S_TL_DESTINATION_SUB = 2
  S_TL_SOURCE_SUB = 3

  [_Last] = 3
End Enum

' _OD_ Output structure for Laws
Public Enum OutputLawsStruct
  S_OD_ID = 1
  S_OD_LEVEL = 2
  S_OD_TITLE = 3
  S_OD_TYPE = 4
  S_OD_GID = 5
End Enum

' _OL_ Output laws structure for Links
Public Enum OutputLinksStruct
  S_OL_SOURCE = 1
  S_OL_TARGET = 2
  S_OL_TYPE = 3
End Enum

Public Function ParseHyperlink(target As Word.Hyperlink) As ItemHyperlink
  Dim iLink As New ItemHyperlink
  If Not iLink.FromAddress(target.Address) Then _
    Exit Function
  Set ParseHyperlink = iLink
End Function

Public Function ParseDocument(theDoc As Word.Document) As InfoDocument
  Dim iDoc As New InfoDocument
  If Not iDoc.Init(theDoc) Then _
    Exit Function
  Set ParseDocument = iDoc
End Function

Public Function ParseLawTitle(sTitle$) As ItemLaw
  Dim titleTxt$: titleTxt = SubstituteWhitespace(sTitle)
  Dim theParser As ParserNPA: Set theParser = GetParserLawTitle
  If Not theParser.Parse(titleTxt) Then _
    Exit Function
  
  Dim dateParser As ParserDate: Set dateParser = GetParserDate
  Debug.Assert dateParser.Test(theParser.data_.date_)
  Call dateParser.Parse(theParser.data_.date_)
  
  Set ParseLawTitle = New ItemLaw
  With ParseLawTitle
    .title_ = ApplyCommonAbbreviations(titleTxt)
    .typeTxt_ = theParser.data_.docType_
    .creator_ = theParser.data_.creator_
    .date_ = dateParser.AsDigitsString
    .number_ = theParser.data_.number_
    .header_ = theParser.data_.title_
    .attach_ = theParser.data_.titleDoc_
    .isScoped_ = theParser.data_.isTimeBound_
      
    .type_ = InferTypeFrom(.typeTxt_, .creator_)
    If .creator_ = vbNullString Then _
      .creator_ = InferCreatorFrom(.type_)
  End With
End Function

Public Function GetDatabaseHash(target As Excel.Workbook) As String
  Dim hashRng As Excel.Range: Set hashRng = target.Names(HT03_CELL_HASH).RefersToRange
  Call hashRng.Calculate
  GetDatabaseHash = hashRng
End Function

Public Function GetDocumentsLocation(target As Excel.Workbook) As String
  Dim iPath As New API_Path: Call iPath.FromString(target.Names(HT03_CELL_FOLDER).RefersToRange)
  Call iPath.ToServer(target.Path)
  GetDocumentsLocation = iPath.Text
  If GetDocumentsLocation = vbNullString Then _
    GetDocumentsLocation = target.Path
End Function

Public Function GetParserLawTitle() As ParserNPA
  Static s_Parser As ParserNPA
  If s_Parser Is Nothing Then _
    Set s_Parser = New ParserNPA
  Set GetParserLawTitle = s_Parser
End Function

Public Function GetParserDate() As ParserDate
  Static s_Parser As ParserDate
  If s_Parser Is Nothing Then _
    Set s_Parser = New ParserDate
  Set GetParserDate = s_Parser
End Function

Public Function InferTypeFrom(sDocType$, sCreator$) As TLawType
  InferTypeFrom = T_LAW_01_UNKNOWN
  
  If sDocType Like "[Кк]онституция*" Then
    InferTypeFrom = T_LAW_03_CONSTITUTION
  ElseIf sDocType Like "*[Зз]акон*" Then
    InferTypeFrom = IIf(sDocType Like "*[Кк]онституционный*", T_LAW_04_FEDERAL_CONST, T_LAW_07_FEDERALLAW)
    Exit Function
  ElseIf sDocType Like "*[Кк]одекс*" Then
    InferTypeFrom = T_LAW_05_CODEX
    Exit Function
  End If
  
  If sCreator = vbNullString Then
    Exit Function
  ElseIf sCreator Like "*[Пп]резидент*" Then
    InferTypeFrom = T_LAW_06_PRESIDENT
    Exit Function
  End If
  
  Dim sFirstCreator$: sFirstCreator = sCreator
  If InStr(sFirstCreator, " и ") <> 0 Then _
    sFirstCreator = Left(sCreator, InStr(sFirstCreator, " и "))
  If InStr(sFirstCreator, ", ") <> 0 Then _
    sFirstCreator = Left(sCreator, InStr(sFirstCreator, ", "))
    
  If sFirstCreator Like "*[Пп]рокуратур*" Or sFirstCreator Like "*[Пп]рокурор*" Then
    InferTypeFrom = T_LAW_10_PROSECUTOR
  ElseIf sFirstCreator Like "*[Пп]равительств*" Then
    InferTypeFrom = T_LAW_08_GOVERNMENT
  ElseIf sFirstCreator Like "* [Сс]уд*" Then
    InferTypeFrom = T_LAW_09_JUSTICE
  ElseIf sFirstCreator Like "*[Мм]инистерств*" Or sFirstCreator Like "Мин*" Then
    InferTypeFrom = T_LAW_11_MINISTRY
  ElseIf sFirstCreator Like "Мин*" Then
    InferTypeFrom = T_LAW_11_MINISTRY
  ElseIf sFirstCreator Like "*служб*" Or sFirstCreator Like "*агентств*" Or sFirstCreator Like "*фонд*" Then
    InferTypeFrom = T_LAW_12_AGENCY
  ElseIf sFirstCreator Like "Гос*" Or sFirstCreator Like "Рос*" Then
    InferTypeFrom = T_LAW_12_AGENCY
  End If
End Function

Public Function InferCreatorFrom(sType As TLawType) As String
  Select Case (sType)
    Case T_LAW_03_CONSTITUTION: InferCreatorFrom = "Российская федерация"
    Case T_LAW_04_FEDERAL_CONST: InferCreatorFrom = "Госдума РФ"
    Case T_LAW_05_CODEX: InferCreatorFrom = "Госдума РФ"
    Case T_LAW_07_FEDERALLAW: InferCreatorFrom = "Госдума РФ"
    Case T_LAW_09_JUSTICE: InferCreatorFrom = "Верховный суд РФ"
    Case T_LAW_10_PROSECUTOR: InferCreatorFrom = "Генеральная прокуратура РФ"
  End Select
End Function

Public Function DefaultLawLevelFor(aType As TLawType) As Long
  Select Case aType
    Case 0: DefaultLawLevelFor = 0
    Case 1: DefaultLawLevelFor = 7
    Case 2 To 3: DefaultLawLevelFor = 1
    Case 4: DefaultLawLevelFor = 2
    Case 5 To 7: DefaultLawLevelFor = 3
    Case 8 To 10: DefaultLawLevelFor = 4
    Case 11: DefaultLawLevelFor = 5
    Case 12: DefaultLawLevelFor = 6
  End Select
End Function

Public Function AddDummyDocument(props As ItemLaw, sDocsPath$, Optional linkSys As TLinkSystem = DEFAULT_LINK_SYSTEM)
  Dim wordWrap As New API_WordWrapper
  Dim newDoc As Word.Document: Set newDoc = wordWrap.NewDocument
  
  Call newDoc.Paragraphs.Add
  newDoc.Paragraphs.First.Range = props.title_
  Dim lnkRange As Word.Range: Set lnkRange = newDoc.Paragraphs.First.Range
  Dim newLink As New ItemHyperlink: Call newLink.Init(linkSys, props.gid_, "sub_0")
  Call lnkRange.Hyperlinks.Add(lnkRange, newLink.ToAddress)
  
  Call newDoc.Paragraphs.Add
  newDoc.Paragraphs.Last.Range = HT03_DUMMY_DOCUMENT_DISCLAIMER
  
  Call newDoc.SaveAs(sDocsPath & "\" & props.fileName_, FileFormat:=wdFormatDocumentDefault)
  Call wordWrap.ReleaseDocument
End Function
