CLASS zcl_abapgit_gui_html_processor DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    CONSTANTS c_css_build_name TYPE string VALUE 'css/bundle.css'.
    CONSTANTS c_preprocess_marker TYPE string VALUE `<!-- abapgit HTML preprocessor -->`.
    CONSTANTS c_comment_start TYPE string VALUE `<!--`.
    CONSTANTS c_comment_end TYPE string VALUE `-->`.

    INTERFACES zif_abapgit_gui_html_processor .

    METHODS constructor
      IMPORTING
        ii_asset_man TYPE REF TO zif_abapgit_gui_asset_manager.

    METHODS preserve_css
      IMPORTING
        !iv_css_url TYPE string .

  PROTECTED SECTION.
  PRIVATE SECTION.
    DATA mt_preserve_css TYPE string_table.
    DATA mi_asset_man TYPE REF TO zif_abapgit_gui_asset_manager.

    METHODS patch_html
      IMPORTING
        iv_html TYPE string
      EXPORTING
        ev_html TYPE string
        et_css_urls TYPE string_table
      RAISING
        zcx_abapgit_exception.

    METHODS is_preserved
      IMPORTING
        !iv_css_url TYPE string
      RETURNING
        VALUE(rv_yes) TYPE abap_bool.

    METHODS find_head_offset
      IMPORTING
        iv_html            TYPE string
      RETURNING
        VALUE(rv_head_end) TYPE i
      RAISING
        zcx_abapgit_exception.

ENDCLASS.



CLASS ZCL_ABAPGIT_GUI_HTML_PROCESSOR IMPLEMENTATION.


  METHOD constructor.
    mi_asset_man = ii_asset_man.
  ENDMETHOD.


  METHOD is_preserved.
    READ TABLE mt_preserve_css TRANSPORTING NO FIELDS WITH KEY table_line = iv_css_url.
    rv_yes = boolc( sy-subrc = 0 ).
  ENDMETHOD.


  METHOD patch_html.

    CONSTANTS lc_css_re TYPE string VALUE `<link\s+rel="stylesheet"\s+type="text/css"\s+href="(\S+)">`.

    DATA lv_head_end TYPE i.
    DATA lo_css_re   TYPE REF TO cl_abap_regex.
    DATA lo_matcher  TYPE REF TO cl_abap_matcher.
    DATA lv_css_path TYPE string.
    DATA lv_marker   TYPE string.

    DATA lv_off TYPE i.
    DATA lv_len TYPE i.
    DATA lv_cur TYPE i.

    DATA lc_css_build TYPE string VALUE '<link rel="stylesheet" type="text/css" href="$BUILD_NAME">'.
    REPLACE FIRST OCCURRENCE OF '$BUILD_NAME' IN lc_css_build WITH c_css_build_name. " Mmmm

    CLEAR: ev_html, et_css_urls.

    lv_head_end = find_head_offset( iv_html ).

    CREATE OBJECT lo_css_re
      EXPORTING
        ignore_case = abap_true
        pattern     = lc_css_re.

    lo_matcher = lo_css_re->create_matcher( text = substring( val = iv_html len = lv_head_end ) ).
    WHILE lo_matcher->find_next( ) = abap_true.
      lv_css_path = lo_matcher->get_submatch( 1 ).
      IF abap_false = is_preserved( lv_css_path ).
        lv_off = lo_matcher->get_offset( ).
        lv_len = lo_matcher->get_length( ).
        ev_html = ev_html && substring( val = iv_html off = lv_cur len = lv_off - lv_cur ).
        ev_html = ev_html && c_comment_start && substring( val = iv_html off = lv_off len = lv_len ) && c_comment_end.
        lv_cur  = lv_off + lv_len.
        APPEND lv_css_path TO et_css_urls.
      ENDIF.
    ENDWHILE.

    ev_html = ev_html && substring( val = iv_html off = lv_cur len = lv_head_end - lv_cur ).
    IF lines( et_css_urls ) > 0.
      lv_marker = cl_abap_char_utilities=>newline
        && `    ` " Assume 4 space indent, maybe improve and detect ?
        && c_preprocess_marker
        && cl_abap_char_utilities=>newline
        && `    `.
      ev_html = ev_html && lv_marker && lc_css_build.
    ENDIF.
    ev_html = ev_html && substring( val = iv_html off = lv_head_end ).

  ENDMETHOD.


  METHOD preserve_css.
    APPEND iv_css_url TO mt_preserve_css.
  ENDMETHOD.


  METHOD zif_abapgit_gui_html_processor~process.

    DATA lo_css_processor TYPE REF TO zcl_abapgit_gui_css_processor.
    DATA lt_css_urls TYPE string_table.
    DATA lv_css_build TYPE string.

    FIELD-SYMBOLS <lv_url> LIKE LINE OF lt_css_urls.

    patch_html(
      EXPORTING
        iv_html = iv_html
      IMPORTING
        ev_html = rv_html
        et_css_urls = lt_css_urls ).

    IF lines( lt_css_urls ) > 0.
      CREATE OBJECT lo_css_processor
        EXPORTING
          ii_asset_manager = mi_asset_man.

      LOOP AT lt_css_urls ASSIGNING <lv_url>.
        lo_css_processor->add_file( <lv_url> ).
      ENDLOOP.

      lv_css_build = lo_css_processor->process( ).

      ii_gui_services->cache_asset(
        iv_url     = |{ c_css_build_name }|
        iv_type    = 'text'
        iv_subtype = 'css'
        iv_text    = lv_css_build ).
    ENDIF.

  ENDMETHOD.

  METHOD find_head_offset.

    rv_head_end = find( val = iv_html regex = |{ cl_abap_char_utilities=>newline }?\\s*</head>| case = abap_false ).
    IF rv_head_end <= 0.
      rv_head_end = find( val = iv_html regex = |</head>| case = abap_false ).
      IF rv_head_end <= 0.
        zcx_abapgit_exception=>raise( 'HTML preprocessor: </head> not found' ).
      ENDIF.
    ENDIF.

  ENDMETHOD.

ENDCLASS.
