/* BASIC2026 portable PDF annotation/form flattening. */
static int b2026_pdf_flat_numbers(const unsigned char *body,size_t n,const char *key,double *out,int count){
    B2026PdfKeyRange r;size_t p;int i;if(!b2026_pdf_tf_find_key(body,n,key,&r)||!r.found)return 0;p=b2026_pdf_tf_space(body,r.value_end,r.value_start);if(p>=r.value_end||body[p]!='[')return 0;++p;
    for(i=0;i<count;++i){char *e;p=b2026_pdf_tf_space(body,r.value_end,p);if(p>=r.value_end)return 0;errno=0;out[i]=strtod((const char*)body+p,&e);if(e==(char*)body+p||errno==ERANGE||!isfinite(out[i]))return 0;p=(size_t)(e-(char*)body);}return 1;
}

static int b2026_pdf_flat_name(const unsigned char *body,size_t n,const char *key,char *out,size_t cap){
    B2026PdfKeyRange r;size_t p,e;if(cap==0U)return 0;out[0]='\0';if(!b2026_pdf_tf_find_key(body,n,key,&r)||!r.found)return 0;p=b2026_pdf_tf_space(body,r.value_end,r.value_start);if(p>=r.value_end||body[p]!='/')return 0;e=++p;while(e<r.value_end&&!b2026_pdf_is_delim(body[e]))++e;if(e-p+2U>cap)return 0;out[0]='/';memcpy(out+1U,body+p,e-p);out[e-p+1U]='\0';return 1;
}

static int b2026_pdf_flat_first_ref(B2026PdfDocument *doc,const unsigned char *s,size_t n){
    size_t p=0U;while(p<n){char *e;long a,g;size_t q,u;if(!isdigit(s[p])){++p;continue;}a=strtol((const char*)s+p,&e,10);if(e==(char*)s+p||a<=0||a>INT_MAX){++p;continue;}q=b2026_pdf_tf_space(s,n,(size_t)(e-(char*)s));if(q>=n||!isdigit(s[q])){++p;continue;}g=strtol((const char*)s+q,&e,10);u=b2026_pdf_tf_space(s,n,(size_t)(e-(char*)s));if(u<n&&s[u]=='R'&&g>=0&&g<=INT_MAX)return b2026_pdf_object_lookup(doc,(int)a,(int)g);++p;}return -1;
}

static int b2026_pdf_flat_appearance(B2026PdfDocument *doc,int annotation_index){
    B2026PdfObject *a=&doc->objects[annotation_index];B2026PdfKeyRange ap,nr,sr;const unsigned char *body;size_t length;int num,gen,oi=-1,api=-1;char state[128];
    if(!b2026_pdf_tf_find_key(a->body,a->body_length,"/AP",&ap)||!ap.found)return -1;body=a->body+ap.value_start;length=ap.value_end-ap.value_start;
    if(b2026_pdf_tf_ref(body,length,&num,&gen)&&(api=b2026_pdf_object_lookup(doc,num,gen))>=0){body=doc->objects[api].body;length=doc->objects[api].body_length;}
    if(!b2026_pdf_tf_find_key(body,length,"/N",&nr)||!nr.found)return -1;body+=nr.value_start;length=nr.value_end-nr.value_start;
    if(b2026_pdf_tf_ref(body,length,&num,&gen))oi=b2026_pdf_object_lookup(doc,num,gen);
    else{
        state[0]='\0';(void)b2026_pdf_flat_name(a->body,a->body_length,"/AS",state,sizeof(state));
        if(state[0]!='\0'&&b2026_pdf_tf_find_key(body,length,state,&sr)&&sr.found&&b2026_pdf_tf_ref(body+sr.value_start,sr.value_end-sr.value_start,&num,&gen))oi=b2026_pdf_object_lookup(doc,num,gen);
        if(oi<0)oi=b2026_pdf_flat_first_ref(doc,body,length);
    }
    if(oi<0||b2026_pdf_memfind(doc->objects[oi].body,doc->objects[oi].body_length,"stream",6U)==NULL)return -1;return oi;
}

static int b2026_pdf_flat_add_xobject(B2026PdfDocument *doc,int page_index,const char *name,int object_number){
    const unsigned char *v;size_t n;char *resources=NULL,*xobjects=NULL,*next=NULL;int ri,number;B2026PdfKeyRange r;
    if(b2026_pdf_tf_inherited_value(doc,page_index,"/Resources",&v,&n))resources=b2026_pdf_tf_value_copy(doc,v,n,1);else resources=b2026_strdup_simple("<< >>");if(resources==NULL)goto fail;
    ri=b2026_pdf_tf_add_body(doc,resources);free(resources);resources=NULL;if(ri<0)goto fail;if(!b2026_pdf_tf_find_key(doc->objects[ri].body,doc->objects[ri].body_length,"/XObject",&r))goto fail;
    if(r.found)xobjects=b2026_pdf_tf_value_copy(doc,doc->objects[ri].body+r.value_start,r.value_end-r.value_start,1);else xobjects=b2026_strdup_simple("<< >>");next=b2026_pdf_tf_dict_add(xobjects,name,object_number);free(xobjects);xobjects=NULL;if(next==NULL||!b2026_pdf_tf_set_key(doc,ri,"/XObject",next,0))goto fail;free(next);next=NULL;
    number=doc->objects[ri].old_number;{char ref[64];snprintf(ref,sizeof(ref),"%d 0 R",number);if(!b2026_pdf_tf_set_key(doc,page_index,"/Resources",ref,0))goto fail;}return 1;
fail:free(resources);free(xobjects);free(next);return 0;
}

static int b2026_pdf_flat_annotation(B2026PdfDocument *doc,int page_index,int annotation_index,int serial,int *painted,int *missing,int *widget_missing){
    B2026PdfObject *a=&doc->objects[annotation_index],*appearance;double rect[4],bbox[4],m[6]={1,0,0,1,0,0},x[4],y[4],lo_x,hi_x,lo_y,hi_y,sx,sy,tx,ty;int ap,i,flags=0,stream;char name[64],payload[512];
    if(!b2026_pdf_flat_numbers(a->body,a->body_length,"/Rect",rect,4))return 1;(void)b2026_pdf_dict_integer(a->body,a->body_length,"/F",&flags);if((flags&1)||(flags&2)||(flags&32))return 1;
    ap=b2026_pdf_flat_appearance(doc,annotation_index);if(ap<0){if(b2026_pdf_dict_name_is(a->body,a->body_length,"/Subtype","Widget"))*widget_missing=1;else if(!b2026_pdf_dict_name_is(a->body,a->body_length,"/Subtype","Link")&&!b2026_pdf_dict_name_is(a->body,a->body_length,"/Subtype","Popup"))++*missing;return 1;}
    appearance=&doc->objects[ap];if(!b2026_pdf_flat_numbers(appearance->body,appearance->body_length,"/BBox",bbox,4)){bbox[0]=bbox[1]=0.0;bbox[2]=fabs(rect[2]-rect[0]);bbox[3]=fabs(rect[3]-rect[1]);}(void)b2026_pdf_flat_numbers(appearance->body,appearance->body_length,"/Matrix",m,6);
    x[0]=m[0]*bbox[0]+m[2]*bbox[1]+m[4];y[0]=m[1]*bbox[0]+m[3]*bbox[1]+m[5];x[1]=m[0]*bbox[2]+m[2]*bbox[1]+m[4];y[1]=m[1]*bbox[2]+m[3]*bbox[1]+m[5];x[2]=m[0]*bbox[0]+m[2]*bbox[3]+m[4];y[2]=m[1]*bbox[0]+m[3]*bbox[3]+m[5];x[3]=m[0]*bbox[2]+m[2]*bbox[3]+m[4];y[3]=m[1]*bbox[2]+m[3]*bbox[3]+m[5];lo_x=hi_x=x[0];lo_y=hi_y=y[0];for(i=1;i<4;++i){if(x[i]<lo_x)lo_x=x[i];if(x[i]>hi_x)hi_x=x[i];if(y[i]<lo_y)lo_y=y[i];if(y[i]>hi_y)hi_y=y[i];}
    if(hi_x-lo_x<=0.0||hi_y-lo_y<=0.0)return 1;if(rect[0]>rect[2]){double t=rect[0];rect[0]=rect[2];rect[2]=t;}if(rect[1]>rect[3]){double t=rect[1];rect[1]=rect[3];rect[3]=t;}sx=(rect[2]-rect[0])/(hi_x-lo_x);sy=(rect[3]-rect[1])/(hi_y-lo_y);tx=rect[0]-sx*lo_x;ty=rect[1]-sy*lo_y;
    snprintf(name,sizeof(name),"/B2026Flat%d",serial);if(!b2026_pdf_flat_add_xobject(doc,page_index,name,appearance->old_number))return 0;snprintf(payload,sizeof(payload),"q %.17g 0 0 %.17g %.17g %.17g cm %s Do Q",sx,sy,tx,ty,name);stream=b2026_pdf_tf_add_stream(doc,payload);if(stream<0||!b2026_pdf_tf_append_contents(doc,page_index,-1,stream))return 0;++*painted;return 1;
}

static int b2026_pdf_flat_document(B2026PdfDocument *doc,int *painted,int *missing,int *widget_missing){
    int page,serial=1;*painted=*missing=*widget_missing=0;for(page=0;page<doc->page_count;++page){int pi=doc->page_objects[page],*annotations=NULL,count=0,cap=0,j;B2026PdfObject *p=&doc->objects[pi];B2026PdfKeyRange r;size_t q;if(!b2026_pdf_tf_find_key(p->body,p->body_length,"/Annots",&r))return 0;if(!r.found)continue;q=r.value_start;while(q<r.value_end){char *e;long a,g;size_t s,u;int oi;if(!isdigit(p->body[q])){++q;continue;}a=strtol((const char*)p->body+q,&e,10);s=b2026_pdf_tf_space(p->body,r.value_end,(size_t)(e-(char*)p->body));if(s>=r.value_end||!isdigit(p->body[s])){++q;continue;}g=strtol((const char*)p->body+s,&e,10);u=b2026_pdf_tf_space(p->body,r.value_end,(size_t)(e-(char*)p->body));if(u<r.value_end&&p->body[u]=='R'&&a>0&&a<=INT_MAX&&g>=0&&g<=INT_MAX&&(oi=b2026_pdf_object_lookup(doc,(int)a,(int)g))>=0){if(count==cap){int nc=cap==0?8:cap*2;int *next=(int*)realloc(annotations,(size_t)nc*sizeof(*next));if(next==NULL){free(annotations);return 0;}annotations=next;cap=nc;}annotations[count++]=oi;q=u+1U;}else ++q;}for(j=0;j<count;++j)if(!b2026_pdf_flat_annotation(doc,pi,annotations[j],serial++,painted,missing,widget_missing)){free(annotations);return 0;}free(annotations);}
    return 1;
}

static const char *b2026_pdf_flatten(int argc,const B2026PdfArg *args){
    int separate=argc==2,rc,i,removed_pages=0,removed=0,painted=0,missing=0,widget_missing=0;const char *source;char *output=NULL,*stage=NULL;B2026PdfDocument doc;B2026PdfPageList pages;const char *result;memset(&doc,0,sizeof(doc));memset(&pages,0,sizeof(pages));
    if((argc!=1&&argc!=2)||args[0].kind!=1||args[0].text==NULL||(separate&&(args[1].kind!=1||args[1].text==NULL)))return b2026_pdf_fail(4185,"","FLATTEN requires path$ or output$, source$ / フラット化にはPDFパス、または保存先と入力元が必要です");source=separate?args[1].text:args[0].text;output=b2026_pdf_output_path(args[0].text);if(output==NULL)goto memory_fail;
    rc=b2026_pdf_load(&doc,source,3);if(rc<=0){b2026_pdf_fail(4186,source,b2026_pdf_load_error(rc));goto fail;}if(!b2026_pdf_flat_document(&doc,&painted,&missing,&widget_missing))goto memory_fail;if(widget_missing){b2026_pdf_fail(4187,source,"a form widget has no usable appearance; flattening was refused to avoid losing its value / 外観を持たないフォームWidgetがあるため値の消失を避けてフラット化を拒否しました");goto fail;}
    if(!b2026_pdf_remove_all_annotations(&doc,&removed_pages))goto memory_fail;if(doc.root_object>=0){static const char *keys[]={"/Type","/Pages","/AcroForm","/Metadata"};int k;for(k=0;k<4;++k)do{if(!b2026_pdf_remove_object_key(&doc.objects[doc.root_object],keys[k],&removed))goto memory_fail;}while(removed);}for(i=0;i<doc.page_count;++i)if(!b2026_pdf_pages_push(&pages,&doc,doc.page_objects[i],-1))goto memory_fail;
    stage=b2026_atomic_temp_path(output);if(stage==NULL||!b2026_pdf_write_output(stage,&doc,1,&pages,0,1,1)){if(stage!=NULL)b2026_atomic_discard_file(stage);b2026_pdf_fail(4188,output,"flattened PDF could not be written atomically or exceeded 2 GiB / フラット化PDFを原子的に保存できないか2GiB上限を超えました");goto fail;}if(!b2026_pdf_annotations_removed(stage)){b2026_atomic_discard_file(stage);b2026_pdf_fail(4189,output,"post-write verification found annotations or form data / 保存後の検証で注釈またはフォーム情報が残っています");goto fail;}if(!b2026_atomic_commit_file(stage,output)){b2026_atomic_discard_file(stage);b2026_pdf_fail(4189,output,"verified flattened PDF could not be committed / 検証済みフラット化PDFを保存先へ反映できません");goto fail;}
    if(missing>0){char warning[256];snprintf(warning,sizeof(warning),"%d件の注釈には使用可能な外観がないため描画せず、対話情報だけを削除しました。 / %d annotations had no usable appearance; only their interactive data was removed.",missing,missing);b2026_pdf_log_warning(warning);}if(painted==0)b2026_pdf_log_warning("描画可能な注釈またはフォーム外観はありませんでした。 / No paintable annotation or form appearances were found.");free(stage);result=runtime_temp_string_copy(output);free(output);free(pages.items);b2026_pdf_document_free(&doc);return result;
memory_fail:b2026_pdf_fail(4199,source,"out of memory / メモリ不足です");
fail:free(stage);free(output);free(pages.items);b2026_pdf_document_free(&doc);return runtime_temp_string_copy("");
}
