/* BASIC2026 recoverable PDF repair and page salvage. */
typedef struct {
    const char *broken_page;
    double media[4];
    int max_diagnostics;
} B2026PdfRepairSettings;

static void b2026_pdf_repair_defaults(B2026PdfRepairSettings *s) {
    s->broken_page="SKIP";s->media[0]=0.0;s->media[1]=0.0;s->media[2]=595.0;s->media[3]=842.0;s->max_diagnostics=1000;
}

static int b2026_pdf_repair_settings(const B2026PdfArg *arg,B2026PdfRepairSettings *s) {
    HashVariable *h=NULL;HashEntry *e;const char *sval;double n;
    b2026_pdf_repair_defaults(s);if(arg==NULL)return 1;if(!b2026_pdf_tf_hash(arg,&h))return 0;
    e=b2026_pdf_tf_entry(h,"最大診断数","maxDiagnostics");if(e!=NULL){if(e->value.type!=TYPE_NUMBER)return 0;n=e->value.val.number;if(!isfinite(n)||floor(n)!=n||n<1.0||n>100000.0)return 0;s->max_diagnostics=(int)n;}
    e=b2026_pdf_tf_entry(h,"破損ページ","brokenPage");if(e!=NULL){if(e->value.type==TYPE_NUMBER)return 0;sval=hash_value_as_text(&e->value);if(b2026_strcasecmp(sval,"SKIP")==0||strcmp(sval,"除外")==0)s->broken_page="SKIP";else if(b2026_strcasecmp(sval,"BLANK")==0||strcmp(sval,"白紙")==0)s->broken_page="BLANK";else return 0;}
    e=b2026_pdf_tf_entry(h,"代替用紙サイズ","fallbackPageSize");if(e!=NULL){if(e->value.type==TYPE_NUMBER)return 0;sval=hash_value_as_text(&e->value);if(b2026_strcasecmp(sval,"A4")==0){s->media[2]=595.0;s->media[3]=842.0;}else if(b2026_strcasecmp(sval,"LETTER")==0){s->media[2]=612.0;s->media[3]=792.0;}else if(b2026_strcasecmp(sval,"LEGAL")==0){s->media[2]=612.0;s->media[3]=1008.0;}else if(b2026_strcasecmp(sval,"A3")==0){s->media[2]=842.0;s->media[3]=1191.0;}else if(b2026_strcasecmp(sval,"B5")==0){s->media[2]=516.0;s->media[3]=729.0;}else return 0;}
    return 1;
}

static void b2026_pdf_repair_result(B2026PdfStructureReport *r,const char *operation,const char *source,const char *output,int ok,int saved,int changed,int partial,int found,int output_pages,int repaired,int recovered,int skipped,int blank,const char *message) {
    b2026_pdf_sc_pair_number(r->target,"取得","ok",ok?1.0:0.0);b2026_pdf_sc_pair_number(r->target,"保存","saved",saved?1.0:0.0);b2026_pdf_sc_pair_string(r->target,"状態","status",saved?(r->warnings>0||partial?"WARNING":"OK"):"FAILED");b2026_pdf_sc_pair_number(r->target,"変更","changed",changed?1.0:0.0);b2026_pdf_sc_pair_number(r->target,"一部救出","partial",partial?1.0:0.0);b2026_pdf_sc_pair_string(r->target,"操作","operation",operation);b2026_pdf_sc_pair_string(r->target,"入力パス","sourcePath",source!=NULL?source:"");b2026_pdf_sc_pair_string(r->target,"出力パス","outputPath",output!=NULL?output:"");b2026_pdf_sc_pair_string(r->target,"内容","message",message!=NULL?message:"");b2026_pdf_sc_pair_number(r->target,"発見ページ数","foundPages",(double)found);b2026_pdf_sc_pair_number(r->target,"出力ページ数","outputPages",(double)output_pages);b2026_pdf_sc_pair_number(r->target,"修復ページ数","repairedPages",(double)repaired);b2026_pdf_sc_pair_number(r->target,"救出ページ数","recoveredPages",(double)recovered);b2026_pdf_sc_pair_number(r->target,"除外ページ数","skippedPages",(double)skipped);b2026_pdf_sc_pair_number(r->target,"白紙化ページ数","blankPages",(double)blank);b2026_pdf_sc_pair_number(r->target,"修復項目数","repairCount",(double)r->information);b2026_pdf_sc_pair_number(r->target,"警告数","warningCount",(double)r->warnings);b2026_pdf_sc_pair_number(r->target,"エラー数","errorCount",(double)r->errors);b2026_pdf_sc_pair_number(r->target,"診断数","diagnosticCount",(double)r->stored);b2026_pdf_sc_pair_number(r->target,"全診断数","totalDiagnosticCount",(double)r->total);b2026_pdf_sc_pair_number(r->target,"診断省略","diagnosticsTruncated",r->total>r->stored?1.0:0.0);
}

static void b2026_pdf_repair_error(int code,const char *operation,const char *source,const char *output,const char *detail) {
    char message[4096];snprintf(message,sizeof(message),"PDF.%s failed: source=%s output=%s: %s",operation!=NULL?operation:"REPAIR",source!=NULL?source:"",output!=NULL?output:"",detail!=NULL?detail:"unknown error / 不明なエラー");basic_set_error_logged(code,message,"PDF",current_source_path,current_line,"PDF");b2026_try_append_log_line("__PDF_ERROR.log",message);
}

static int b2026_pdf_repair_hash_number(const char *target,const char *key,double *value) {
    HashVariable *h=ensure_local_hash(get_hash_frame(target),target);HashEntry *e=find_hash_entry(h,key);if(e==NULL||e->value.type!=TYPE_NUMBER)return 0;*value=e->value.val.number;return 1;
}

static int b2026_pdf_repair_check(const char *path,int *errors,int *warnings) {
    B2026PdfArg a;double value;const char *name="__PDF_REPAIR_VERIFY_INTERNAL";HashVariable *h;
    memset(&a,0,sizeof(a));a.kind=1;a.text=path;if(!b2026_pdf_structure_check(name,1,&a))return 0;
    if(!b2026_pdf_repair_hash_number(name,"errorCount",&value))return 0;*errors=(int)value;
    if(!b2026_pdf_repair_hash_number(name,"warningCount",&value))return 0;*warnings=(int)value;
    h=ensure_local_hash(get_hash_frame(name),name);clear_hash_entries(h);return 1;
}

static int b2026_pdf_repair_atomic_copy(const char *target,const unsigned char *data,size_t n) {
    char *temp=b2026_atomic_temp_path(target),detail[512];FILE *fp;int ok=0;
    if(temp==NULL||!saveimage_ensure_directories_safe(target,detail,sizeof(detail))){free(temp);return 0;}fp=b2026_fopen(temp,"wb");if(fp==NULL){free(temp);return 0;}b2026_io_hard_guard_begin("PDF",target,"WRITE");if(n<(size_t)B2026_PDF_EDIT_MAX_BYTES&&(n==0U||fwrite(data,1U,n,fp)==n)&&fflush(fp)==0&&!ferror(fp))ok=1;b2026_io_hard_guard_end();if(fclose(fp)!=0)ok=0;if(ok)ok=b2026_atomic_commit_file(temp,target);if(!ok)b2026_atomic_discard_file(temp);free(temp);return ok;
}

static int b2026_pdf_repair_scan_pages(B2026PdfDocument *doc) {
    int i;doc->page_count=0;
    for(i=0;i<doc->object_count;++i)if(b2026_pdf_dict_name_is(doc->objects[i].body,doc->objects[i].body_length,"/Type","Page"))if(!b2026_pdf_page_append(doc,i))return 0;
    return doc->page_count>0;
}

static int b2026_pdf_repair_page_has_missing_ref(B2026PdfDocument *doc,int object_index,const char *key) {
    int number,generation;
    B2026PdfObject *o;
    if(doc==NULL||object_index<0||object_index>=doc->object_count)return 1;
    o=&doc->objects[object_index];
    return b2026_pdf_named_ref(o->body,o->body_length,key,&number,&generation)&&
           b2026_pdf_object_lookup(doc,number,generation)<0;
}

static int b2026_pdf_repair_page_usable(B2026PdfDocument *doc,int object_index) {
    B2026PdfObject *o;
    if(doc==NULL||object_index<0||object_index>=doc->object_count)return 0;
    o=&doc->objects[object_index];
    if(!b2026_pdf_dict_name_is(o->body,o->body_length,"/Type","Page"))return 0;
    if(b2026_pdf_repair_page_has_missing_ref(doc,object_index,"/Contents"))return 0;
    if(b2026_pdf_repair_page_has_missing_ref(doc,object_index,"/Resources"))return 0;
    return 1;
}

static int b2026_pdf_repair_verify_output(const char *path,int *warnings) {
    int errors=0;return b2026_pdf_repair_check(path,&errors,warnings)&&errors==0;
}

static int b2026_pdf_repair(const char *target_name,const char *operation,int argc,const B2026PdfArg *args) {
    HashVariable *target;B2026PdfStructureReport report;B2026PdfRepairSettings settings;B2026PdfDocument doc;B2026PdfPageList pages;char *output=NULL,*stage=NULL;const char *source="";int rc=0,i,source_errors=0,source_warnings=0,source_checked=0,inferred=0,changed=0,partial=0,repaired=0,recovered=0,skipped=0,blank=0,saved=0,verify_warnings=0;const char *message="";
    memset(&doc,0,sizeof(doc));memset(&pages,0,sizeof(pages));memset(&report,0,sizeof(report));
    b2026_pdf_repair_defaults(&settings);
    if(target_name==NULL||*target_name=='\0')return 0;target=ensure_local_hash(get_hash_frame(target_name),target_name);clear_hash_entries(target);report.target=target_name;report.maximum=1000;
    if(argc<2||argc>3||args[0].kind!=1||args[1].kind!=1||args[0].text==NULL||args[1].text==NULL||!b2026_pdf_repair_settings(argc==3?&args[2]:NULL,&settings)){
        report.maximum=settings.max_diagnostics;b2026_pdf_sc_diag(&report,"ERROR","ARGUMENT_INVALID","OPERATION","repair/recovery arguments are invalid / 修復・救出の引数が正しくありません",-1,-1,0,-1.0,NULL);b2026_pdf_repair_error(4190,operation,"","","invalid repair/recovery arguments / 修復・救出の引数が不正です");b2026_pdf_repair_result(&report,operation,"","",0,0,0,0,0,0,0,0,0,0,"引数が正しくありません");return 0;
    }
    report.maximum=settings.max_diagnostics;source=args[1].text;output=b2026_pdf_output_path(args[0].text);
    if(output==NULL||*source=='\0'||strcmp(output,source)==0){b2026_pdf_sc_diag(&report,"ERROR","PATH_INVALID","FILE","input and output paths must be different / 入力と出力には異なるパスが必要です",-1,-1,0,-1.0,NULL);b2026_pdf_repair_error(4191,operation,source,output,"input and output paths must be different / 入出力に同じパスは指定できません");message="入力と出力には異なるパスを指定してください";goto finish;}
    rc=b2026_pdf_load(&doc,source,2);
    if(rc==-2||rc==-3||rc==-4){const char *detail=b2026_pdf_load_error(rc);b2026_pdf_sc_diag(&report,"ERROR",rc==-2?"ENCRYPTED":(rc==-3?"SIGNED":"UNSUPPORTED_FEATURE"),"FEATURE",detail,-1,-1,0,-1.0,NULL);b2026_pdf_repair_error(4193,operation,source,output,detail);message=detail;goto finish;}
    if(doc.object_count==0){const char *detail=b2026_pdf_load_error(rc);b2026_pdf_sc_diag(&report,"ERROR","SOURCE_UNREADABLE","FILE",detail,-1,-1,0,-1.0,NULL);b2026_pdf_repair_error(4192,operation,source,output,detail);message=detail;goto finish;}
    source_checked=b2026_pdf_repair_check(source,&source_errors,&source_warnings);
    if(rc>0&&source_checked&&source_errors==0&&source_warnings==0){
        if(!b2026_pdf_repair_atomic_copy(output,doc.bytes,doc.length)){b2026_pdf_sc_diag(&report,"ERROR","WRITE_FAILED","FILE","output could not be written atomically / 出力PDFを原子的に保存できません",-1,-1,0,-1.0,NULL);b2026_pdf_repair_error(4197,operation,source,output,"output could not be written atomically / 出力PDFを保存できません");message="出力PDFを保存できません";goto finish;}
        saved=1;if(strcmp(operation,"RECOVER")==0)recovered=doc.page_count;message=strcmp(operation,"RECOVER")==0?"救出を必要とする構造上の問題はありませんでした":"修復が必要な構造上の問題はありませんでした";goto finish;
    }
    if(doc.page_count==0){if(!b2026_pdf_repair_scan_pages(&doc)){b2026_pdf_sc_diag(&report,"ERROR","NO_RECOVERABLE_PAGE","PAGE","no recoverable page was found / 救出可能なページがありません",-1,-1,0,-1.0,NULL);b2026_pdf_repair_error(4194,operation,source,output,"no recoverable page was found / 救出可能なページがありません");message="救出可能なページがありません";goto finish;}inferred=1;changed=1;b2026_pdf_sc_diag(&report,"WARNING","PAGE_ORDER_INFERRED","PAGE","page order was inferred from object order / ページ順をオブジェクト順から推定しました",-1,-1,0,-1.0,NULL);}
    for(i=0;i<doc.page_count;++i){int oi=doc.page_objects[i],rotation;double media[4];B2026PdfPage *page;
        if(!b2026_pdf_repair_page_usable(&doc,oi)){
            partial=1;changed=1;
            if(strcmp(settings.broken_page,"BLANK")==0){
                if(!b2026_pdf_pages_push(&pages,&doc,-1,-1)){b2026_pdf_repair_error(4199,operation,source,output,"out of memory / メモリ不足です");message="メモリ不足です";goto finish;}
                page=&pages.items[pages.count-1];
                if(oi>=0&&oi<doc.object_count&&b2026_pdf_page_box(&doc,oi,"/MediaBox",media)&&media[2]>media[0]&&media[3]>media[1])memcpy(page->media,media,sizeof(media));else memcpy(page->media,settings.media,sizeof(settings.media));
                page->has_media=1;++blank;b2026_pdf_sc_diag(&report,"WARNING","BROKEN_PAGE_BLANKED","PAGE","a page with missing required references was replaced by a blank page / 必要な参照が欠けたページを白紙へ置き換えました",oi>=0&&oi<doc.object_count?doc.objects[oi].old_number:-1,oi>=0&&oi<doc.object_count?doc.objects[oi].old_generation:-1,i+1,-1.0,NULL);
            }else{++skipped;b2026_pdf_sc_diag(&report,"WARNING","BROKEN_PAGE_SKIPPED","PAGE","a page with missing required references was excluded / 必要な参照が欠けたページを除外しました",oi>=0&&oi<doc.object_count?doc.objects[oi].old_number:-1,oi>=0&&oi<doc.object_count?doc.objects[oi].old_generation:-1,i+1,-1.0,NULL);}
            continue;
        }
        if(!b2026_pdf_pages_push(&pages,&doc,oi,-1)){b2026_pdf_repair_error(4199,operation,source,output,"out of memory / メモリ不足です");message="メモリ不足です";goto finish;}
        page=&pages.items[pages.count-1];
        if(!b2026_pdf_page_box(&doc,oi,"/MediaBox",media)||!isfinite(media[0])||!isfinite(media[1])||!isfinite(media[2])||!isfinite(media[3])||media[2]<=media[0]||media[3]<=media[1]){memcpy(page->media,settings.media,sizeof(settings.media));page->has_media=1;changed=1;++repaired;b2026_pdf_sc_diag(&report,"INFO","MEDIABOX_DEFAULTED","PAGE","missing or invalid MediaBox was replaced with the fallback size / MediaBoxを代替用紙サイズで補完しました",doc.objects[oi].old_number,doc.objects[oi].old_generation,i+1,-1.0,"/MediaBox");}
        rotation=b2026_pdf_page_inherited_integer(&doc,oi,"/Rotate",0);if(rotation%90!=0){int normalized=(int)(floor(((double)rotation/90.0)+0.5)*90.0);page->rotation_override=((normalized%360)+360)%360;changed=1;++repaired;b2026_pdf_sc_diag(&report,"INFO","ROTATE_NORMALIZED","PAGE","invalid page rotation was normalized / 不正なページ回転を正規化しました",doc.objects[oi].old_number,doc.objects[oi].old_generation,i+1,-1.0,"/Rotate");}
    }
    if(pages.count==0){b2026_pdf_sc_diag(&report,"ERROR","NO_RECOVERABLE_PAGE","PAGE","no recoverable page was found / 救出可能なページがありません",-1,-1,0,-1.0,NULL);b2026_pdf_repair_error(4194,operation,source,output,"no recoverable page was found / 救出可能なページがありません");message="救出可能なページがありません";goto finish;}
    if(strcmp(operation,"RECOVER")==0){recovered=pages.count-blank;changed=1;if(inferred||skipped>0||blank>0)partial=1;}
    if(source_errors>0||rc<=0){changed=1;b2026_pdf_sc_diag(&report,"INFO","DOCUMENT_STRUCTURE_REBUILT","DOCUMENT","xref, trailer, Catalog, and Pages structures were rebuilt / xref・trailer・Catalog・Pages構造を再構築しました",-1,-1,0,-1.0,NULL);}
    stage=b2026_atomic_temp_path(output);if(stage==NULL||!b2026_pdf_write_output(stage,&doc,1,&pages,0,1,0)){b2026_pdf_sc_diag(&report,"ERROR","WRITE_FAILED","FILE","temporary repaired PDF could not be written / 修復PDFの一時出力に失敗しました",-1,-1,0,-1.0,NULL);b2026_pdf_repair_error(4197,operation,source,output,"temporary repaired PDF could not be written / 修復PDFを保存できません");message="修復PDFを保存できません";goto finish;}
    if(!b2026_pdf_repair_verify_output(stage,&verify_warnings)){b2026_pdf_sc_diag(&report,"ERROR","POSTCHECK_FAILED","DOCUMENT","repaired PDF failed its structural verification / 修復後PDFの構造検査に失敗しました",-1,-1,0,-1.0,NULL);b2026_pdf_repair_error(4196,operation,source,output,"repaired PDF failed structural verification / 修復後の構造検査に失敗しました");b2026_atomic_discard_file(stage);message="修復後の構造検査に失敗しました";goto finish;}
    if(!b2026_atomic_commit_file(stage,output)){b2026_pdf_sc_diag(&report,"ERROR","WRITE_FAILED","FILE","verified PDF could not be committed / 検査済みPDFを保存先へ反映できません",-1,-1,0,-1.0,NULL);b2026_pdf_repair_error(4197,operation,source,output,"verified PDF could not be committed / 検査済みPDFを保存できません");b2026_atomic_discard_file(stage);message="検査済みPDFを保存できません";goto finish;}
    saved=1;if(verify_warnings>0)b2026_pdf_sc_diag(&report,"WARNING","POSTCHECK_WARNING","DOCUMENT","repaired PDF has non-fatal structural warnings / 修復後PDFに非致命的な警告があります",-1,-1,0,-1.0,NULL);message=strcmp(operation,"RECOVER")==0?"読み取れるページを新しいPDFへ救出しました":"PDF構造を修復しました";
finish:
    b2026_pdf_repair_result(&report,operation,source,output!=NULL?output:"",saved?1:0,saved,changed,partial,doc.page_count,pages.count,repaired,recovered,skipped,blank,message);
    b2026_pdf_document_free(&doc);free(pages.items);free(stage);free(output);return saved;
}
