/* BASIC2026 portable PDF JavaScript remover. */
typedef struct {
    int javascript_removed,trigger_removed,action_objects_neutralized;
    int name_trees_removed,xfa_removed,verified;
} B2026PdfJavaScriptStats;

static void b2026_pdf_js_pair_number(const char *target,const char *jp,const char *en,double value){
    if(target==NULL)return;hash_set_number(target,jp,value);hash_set_number(target,en,value);
}

static void b2026_pdf_js_pair_string(const char *target,const char *jp,const char *en,const char *value){
    if(target==NULL)return;hash_set_string(target,jp,value!=NULL?value:"");hash_set_string(target,en,value!=NULL?value:"");
}

static void b2026_pdf_js_report(const char *target,const char *path,int ok,const B2026PdfJavaScriptStats *st){
    HashVariable *h;if(target==NULL||*target=='\0')return;h=ensure_local_hash(get_hash_frame(target),target);clear_hash_entries(h);
    b2026_pdf_js_pair_number(target,"成功","ok",ok?1.0:0.0);
    b2026_pdf_js_pair_string(target,"保存先","path",ok&&path!=NULL?path:"");
    b2026_pdf_js_pair_number(target,"JavaScript削除数","javascriptRemoved",st!=NULL?(double)st->javascript_removed:0.0);
    b2026_pdf_js_pair_number(target,"起動動作削除数","triggersRemoved",st!=NULL?(double)st->trigger_removed:0.0);
    b2026_pdf_js_pair_number(target,"動作無効化数","actionsNeutralized",st!=NULL?(double)st->action_objects_neutralized:0.0);
    b2026_pdf_js_pair_number(target,"名前ツリー削除数","nameTreesRemoved",st!=NULL?(double)st->name_trees_removed:0.0);
    b2026_pdf_js_pair_number(target,"XFA削除数","xfaRemoved",st!=NULL?(double)st->xfa_removed:0.0);
    b2026_pdf_js_pair_string(target,"検証結果","verification",ok&&st!=NULL&&st->verified?"PASS":"FAIL");
}

static int b2026_pdf_js_object_has_javascript(B2026PdfDocument *doc,int oi,int depth);

static int b2026_pdf_js_value_has_javascript(B2026PdfDocument *doc,const unsigned char *value,size_t n,int depth){
    size_t p=0U;if(depth>32||value==NULL)return 0;
    if(b2026_pdf_memfind(value,n,"/JavaScript",11U)!=NULL||b2026_pdf_memfind(value,n,"/JS",3U)!=NULL)return 1;
    while(p<n){
        size_t e;int num,gen,oi;
        p=b2026_pdf_tf_space(value,n,p);if(p>=n)break;
        if(value[p]=='('||(value[p]=='<'&&(p+1U>=n||value[p+1U]!='<'))){p=b2026_pdf_tf_value_end(value,n,p,0);continue;}
        if(value[p]=='<'&&p+1U<n&&value[p+1U]=='<'){p+=2U;continue;}
        if(b2026_pdf_tf_ref(value+p,n-p,&num,&gen)&&(oi=b2026_pdf_object_lookup(doc,num,gen))>=0){
            if(b2026_pdf_js_object_has_javascript(doc,oi,depth+1))return 1;
            e=b2026_pdf_tf_value_end(value,n,p,0);p=e>p?e:p+1U;continue;
        }
        ++p;
    }
    return 0;
}

static int b2026_pdf_js_object_has_javascript(B2026PdfDocument *doc,int oi,int depth){
    B2026PdfObject *o;B2026PdfKeyRange r;if(depth>32||oi<0||oi>=doc->object_count)return 0;o=&doc->objects[oi];
    if(b2026_pdf_dict_name_is(o->body,o->body_length,"/S","JavaScript"))return 1;
    if(b2026_pdf_tf_find_key(o->body,o->body_length,"/JS",&r)&&r.found)return 1;
    return b2026_pdf_js_value_has_javascript(doc,o->body,o->body_length,depth+1);
}

static int b2026_pdf_js_key_has_javascript(B2026PdfDocument *doc,int oi,const char *key,int *present){
    B2026PdfKeyRange r;B2026PdfObject *o;if(present!=NULL)*present=0;if(oi<0||oi>=doc->object_count)return 0;o=&doc->objects[oi];
    if(!b2026_pdf_tf_find_key(o->body,o->body_length,key,&r))return 0;if(!r.found)return 1;if(present!=NULL)*present=1;
    return b2026_pdf_js_value_has_javascript(doc,o->body+r.value_start,r.value_end-r.value_start,0)?2:1;
}

static int b2026_pdf_js_remove_trigger(B2026PdfDocument *doc,int oi,const char *key,int *removed){
    int present=0,kind=b2026_pdf_js_key_has_javascript(doc,oi,key,&present);if(kind==0)return 0;
    if(present&&kind==2){if(!b2026_pdf_tf_set_key(doc,oi,key,NULL,1))return 0;++*removed;}return 1;
}

static int b2026_pdf_js_strip(B2026PdfDocument *doc,B2026PdfJavaScriptStats *st){
    B2026PdfSanitizeSettings settings;B2026PdfSanitizeStats san;int i,removed=0;
    static const char *triggers[]={"/OpenAction","/A","/AA","/Next"};
    memset(&settings,0,sizeof(settings));memset(&san,0,sizeof(san));settings.javascript=1;
    if(!b2026_pdf_san_strip_names(doc,&settings,&san))return 0;st->name_trees_removed+=san.javascript_removed;
    for(i=0;i<doc->object_count;++i){int k;
        for(k=0;k<4;++k)if(!b2026_pdf_js_remove_trigger(doc,i,triggers[k],&st->trigger_removed))return 0;
        removed=0;if(!b2026_pdf_san_remove_key(doc,i,"/JavaScript",&removed))return 0;st->javascript_removed+=removed;
        removed=0;if(!b2026_pdf_san_remove_key(doc,i,"/XFA",&removed))return 0;st->xfa_removed+=removed;
    }
    for(i=0;i<doc->object_count;++i){B2026PdfObject *o=&doc->objects[i];
        if(b2026_pdf_dict_name_is(o->body,o->body_length,"/S","JavaScript")){
            if(!b2026_pdf_san_remove_key(doc,i,"/S",NULL)||!b2026_pdf_san_remove_key(doc,i,"/JS",NULL)||!b2026_pdf_san_remove_key(doc,i,"/Next",NULL))return 0;
            ++st->action_objects_neutralized;
        }
    }
    st->javascript_removed+=st->trigger_removed+st->action_objects_neutralized+st->name_trees_removed+st->xfa_removed;
    return 1;
}

static int b2026_pdf_js_verify(const char *path,int page_count,int had_form,int had_attachments,int had_bookmarks,int had_info){
    B2026PdfDocument d;int rc,i;memset(&d,0,sizeof(d));rc=b2026_pdf_load(&d,path,0);if(rc<=0||d.page_count!=page_count)goto fail;
    if(b2026_pdf_san_names_has_key(&d,"/JavaScript"))goto fail;
    if(had_form&&!b2026_pdf_memfind(d.bytes,d.length,"/AcroForm",9U))goto fail;
    if(had_attachments&&!b2026_pdf_memfind(d.bytes,d.length,"/EmbeddedFiles",14U))goto fail;
    if(had_bookmarks&&!b2026_pdf_memfind(d.bytes,d.length,"/Outlines",9U))goto fail;
    if(had_info&&d.info_object<0)goto fail;
    for(i=0;i<d.object_count;++i){B2026PdfObject *o=&d.objects[i];B2026PdfKeyRange r;
        if(b2026_pdf_dict_name_is(o->body,o->body_length,"/S","JavaScript"))goto fail;
        if(b2026_pdf_tf_find_key(o->body,o->body_length,"/JS",&r)&&r.found)goto fail;
        if(b2026_pdf_tf_find_key(o->body,o->body_length,"/JavaScript",&r)&&r.found)goto fail;
        if(b2026_pdf_tf_find_key(o->body,o->body_length,"/XFA",&r)&&r.found)goto fail;
        if(b2026_pdf_memfind(o->body,o->body_length,"/S /JavaScript",14U)!=NULL)goto fail;
    }
    b2026_pdf_document_free(&d);return 1;
fail:b2026_pdf_document_free(&d);return 0;
}

static const char *b2026_pdf_javascript_remove(const char *target_name,int argc,const B2026PdfArg *args){
    int separate,source_index,rc,i,had_form,had_attachments,had_bookmarks,had_info;const char *source,*result;char *output=NULL,*stage=NULL;
    B2026PdfDocument doc;B2026PdfPageList pages;B2026PdfJavaScriptStats stats;memset(&doc,0,sizeof(doc));memset(&pages,0,sizeof(pages));memset(&stats,0,sizeof(stats));
    if((argc!=1&&argc!=2)||args[0].kind!=1||args[0].text==NULL||(argc==2&&(args[1].kind!=1||args[1].text==NULL))){b2026_pdf_js_report(target_name,"",0,&stats);return b2026_pdf_fail(4426,"","REMOVEJAVASCRIPT requires path$ or output$, source$ / JavaScript削除にはPDFパス、または保存先と入力元が必要です");}
    separate=argc==2;source_index=separate?1:0;source=args[source_index].text;output=b2026_pdf_output_path(args[0].text);if(output==NULL)goto memory_fail;
    rc=b2026_pdf_load(&doc,source,4);if(rc<=0){b2026_pdf_fail(4427,source,b2026_pdf_load_error(rc));goto fail;}
    if(b2026_pdf_memfind(doc.bytes,doc.length,"/ByteRange",10U)!=NULL){b2026_pdf_fail(4428,source,"digitally signed PDF cannot be modified / 電子署名付きPDFは変更できません");goto fail;}
    had_form=b2026_pdf_memfind(doc.bytes,doc.length,"/AcroForm",9U)!=NULL;had_attachments=b2026_pdf_memfind(doc.bytes,doc.length,"/EmbeddedFiles",14U)!=NULL;had_bookmarks=b2026_pdf_memfind(doc.bytes,doc.length,"/Outlines",9U)!=NULL;had_info=doc.info_object>=0;
    if(!b2026_pdf_js_strip(&doc,&stats))goto memory_fail;
    if(!b2026_pdf_tf_set_key(&doc,doc.root_object,"/Type",NULL,1)||!b2026_pdf_tf_set_key(&doc,doc.root_object,"/Pages",NULL,1))goto memory_fail;
    for(i=0;i<doc.page_count;++i)if(!b2026_pdf_pages_push(&pages,&doc,doc.page_objects[i],-1))goto memory_fail;
    if(!b2026_pdf_select_reachable_objects(&doc,1,&pages,1)||!b2026_pdf_san_reenable_annotations(&doc))goto memory_fail;
    stage=b2026_atomic_temp_path(output);if(stage==NULL||!b2026_pdf_write_output(stage,&doc,1,&pages,0,0,1)){b2026_pdf_fail(4429,output,"JavaScript-free PDF could not be staged or exceeded 2 GiB / JavaScript削除済みPDFを準備できないか2GiB上限を超えました");goto fail;}
    if(!b2026_pdf_js_verify(stage,doc.page_count,had_form,had_attachments,had_bookmarks,had_info)){b2026_pdf_fail(4430,output,"post-write JavaScript removal verification failed / JavaScript削除後の検証に失敗しました");goto fail;}stats.verified=1;
    if(!b2026_atomic_commit_file(stage,output)){b2026_pdf_fail(4431,output,"verified PDF could not be committed / 検証済みPDFを保存先へ反映できません");goto fail;}free(stage);stage=NULL;
    b2026_pdf_js_report(target_name,output,1,&stats);result=runtime_temp_string_copy(output);free(output);free(pages.items);b2026_pdf_document_free(&doc);return result;
memory_fail:b2026_pdf_fail(4432,source,"out of memory or unsupported PDF action structure / メモリ不足または未対応のPDF動作構造です");
fail:if(stage!=NULL){b2026_atomic_discard_file(stage);free(stage);}b2026_pdf_js_report(target_name,"",0,&stats);free(output);free(pages.items);b2026_pdf_document_free(&doc);return runtime_temp_string_copy("");
}
