/* BASIC2026 portable PDF rotation normalizer.
 * Existing page rotation is baked into page content, page boxes, and
 * annotation coordinates so that the displayed result does not change. */
static int b2026_pdf_nr_target(HashVariable *h,int *target){
    double value=0.0;int present=0,n;if(h!=NULL&&!b2026_pdf_tf_number(h,"回転","rotation",&value,&present))return 0;if(!present){*target=0;return 1;}if(value<(double)INT_MIN||value>(double)INT_MAX||floor(value)!=value)return 0;n=(int)value;if(n!=0&&n!=90&&n!=180&&n!=270)return 0;*target=n;return 1;
}

static void b2026_pdf_nr_point(int source_rotation,const double source_box[4],int target_rotation,const double target_box[4],double x,double y,double *ox,double *oy){
    double u,v;b2026_pdf_tf_raw_to_display(source_rotation,source_box,x,y,&u,&v);b2026_pdf_tf_display_to_raw(target_rotation,target_box,u,v,ox,oy);
}

static void b2026_pdf_nr_matrix(int source_rotation,const double source_box[4],int target_rotation,const double target_box[4],double *a,double *b,double *c,double *d,double *e,double *f){
    double x0,y0,x1,y1,x2,y2;b2026_pdf_nr_point(source_rotation,source_box,target_rotation,target_box,source_box[0],source_box[1],&x0,&y0);b2026_pdf_nr_point(source_rotation,source_box,target_rotation,target_box,source_box[0]+1.0,source_box[1],&x1,&y1);b2026_pdf_nr_point(source_rotation,source_box,target_rotation,target_box,source_box[0],source_box[1]+1.0,&x2,&y2);*a=x1-x0;*b=y1-y0;*c=x2-x0;*d=y2-y0;*e=x0-*a*source_box[0]-*c*source_box[1];*f=y0-*b*source_box[0]-*d*source_box[1];if(fabs(*a)<1.0e-12)*a=0.0;if(fabs(*b)<1.0e-12)*b=0.0;if(fabs(*c)<1.0e-12)*c=0.0;if(fabs(*d)<1.0e-12)*d=0.0;if(fabs(*e)<1.0e-12)*e=0.0;if(fabs(*f)<1.0e-12)*f=0.0;
}

static int b2026_pdf_nr_set_box(B2026PdfDocument *doc,int page,const char *key,const double old_box[4],double a,double b,double c,double d,double e,double f){
    double box[4];char value[192];b2026_pdf_ns_box_affine(old_box,a,b,c,d,e,f,box);snprintf(value,sizeof(value),"[%.12f %.12f %.12f %.12f]",box[0],box[1],box[2],box[3]);return b2026_pdf_tf_set_key(doc,page,key,value,0);
}

static int b2026_pdf_nr_page(B2026PdfDocument *doc,int page,int target_rotation){
    int source_rotation=b2026_pdf_page_inherited_integer(doc,page,"/Rotate",0),has_crop,k,pre,post;double media[4],crop[4],source_box[4],target_box[4],display_width,display_height,a,b,c,d,e,f,sub_boxes[3][4];int has_sub[3];const char *sub_keys[3]={"/TrimBox","/BleedBox","/ArtBox"};char payload[320],rotation[32];
    if(source_rotation%90!=0)return 0;source_rotation=((source_rotation%360)+360)%360;if(!b2026_pdf_page_box(doc,page,"/MediaBox",media)||media[2]<=media[0]||media[3]<=media[1])return 0;has_crop=b2026_pdf_page_box(doc,page,"/CropBox",crop);if(!has_crop)memcpy(crop,media,sizeof(crop));if(crop[2]<=crop[0]||crop[3]<=crop[1])return 0;for(k=0;k<3;++k)has_sub[k]=b2026_pdf_page_box(doc,page,sub_keys[k],sub_boxes[k]);
    if(source_rotation==target_rotation){snprintf(rotation,sizeof(rotation),"%d",target_rotation);return b2026_pdf_tf_set_key(doc,page,"/Rotate",rotation,0);}
    memcpy(source_box,crop,sizeof(source_box));display_width=(source_rotation==90||source_rotation==270)?source_box[3]-source_box[1]:source_box[2]-source_box[0];display_height=(source_rotation==90||source_rotation==270)?source_box[2]-source_box[0]:source_box[3]-source_box[1];target_box[0]=target_box[1]=0.0;if(target_rotation==90||target_rotation==270){target_box[2]=display_height;target_box[3]=display_width;}else{target_box[2]=display_width;target_box[3]=display_height;}
    b2026_pdf_nr_matrix(source_rotation,source_box,target_rotation,target_box,&a,&b,&c,&d,&e,&f);snprintf(payload,sizeof(payload),"q %.12f %.12f %.12f %.12f %.12f %.12f cm",a,b,c,d,e,f);pre=b2026_pdf_tf_add_stream(doc,payload);post=b2026_pdf_tf_add_stream(doc,"Q");if(pre<0||post<0||!b2026_pdf_tf_append_contents(doc,page,pre,post)||!b2026_pdf_ns_annotations(doc,page,a,b,c,d,e,f))return 0;
    if(!b2026_pdf_nr_set_box(doc,page,"/MediaBox",media,a,b,c,d,e,f)||!b2026_pdf_nr_set_box(doc,page,"/CropBox",crop,a,b,c,d,e,f))return 0;for(k=0;k<3;++k)if(has_sub[k]&&!b2026_pdf_nr_set_box(doc,page,sub_keys[k],sub_boxes[k],a,b,c,d,e,f))return 0;snprintf(rotation,sizeof(rotation),"%d",target_rotation);return b2026_pdf_tf_set_key(doc,page,"/Rotate",rotation,0);
}

static int b2026_pdf_nr_verify(const char *path,int target_rotation,int expected_pages){
    B2026PdfDocument doc;int rc,i;memset(&doc,0,sizeof(doc));rc=b2026_pdf_load(&doc,path,0);if(rc<=0||doc.page_count!=expected_pages){b2026_pdf_log_warning("回転統一PDFを再読込できないかページ数が一致しません。 / Rotation-normalized PDF reload or page-count verification failed.");b2026_pdf_document_free(&doc);return 0;}for(i=0;i<doc.page_count;++i){int oi=doc.page_objects[i],rotation=b2026_pdf_page_inherited_integer(&doc,oi,"/Rotate",0);double media[4],crop[4];rotation=((rotation%360)+360)%360;if(rotation!=target_rotation){b2026_pdf_log_warning("回転統一PDFの回転値が一致しません。 / Rotation-normalized PDF has an unexpected Rotate value.");b2026_pdf_document_free(&doc);return 0;}if(!b2026_pdf_page_box(&doc,oi,"/MediaBox",media)){b2026_pdf_log_warning("回転統一PDFのMediaBoxが不正です。 / Rotation-normalized PDF has an invalid MediaBox.");b2026_pdf_document_free(&doc);return 0;}if(!b2026_pdf_page_box(&doc,oi,"/CropBox",crop))memcpy(crop,media,sizeof(crop));if(media[2]<=media[0]||media[3]<=media[1]||crop[2]<=crop[0]||crop[3]<=crop[1]){b2026_pdf_log_warning("回転統一PDFのページボックスが不正です。 / Rotation-normalized PDF has an invalid page box.");b2026_pdf_document_free(&doc);return 0;}}b2026_pdf_document_free(&doc);return 1;
}

static int b2026_pdf_nr_upright_page(B2026PdfDocument *doc,int page,const B2026PdfNormalizeSettings *settings){
    if(!b2026_pdf_tf_set_key(doc,page,"/Rotate","0",0))return 0;
    return b2026_pdf_ns_page(doc,page,settings);
}

static int b2026_pdf_nr_upright_verify(const char *path,const B2026PdfNormalizeSettings *settings,int expected_pages){
    B2026PdfDocument doc;int rc,i;memset(&doc,0,sizeof(doc));
    if(!b2026_pdf_ns_verify(path,settings,expected_pages))return 0;
    rc=b2026_pdf_load(&doc,path,0);if(rc<=0||doc.page_count!=expected_pages){b2026_pdf_document_free(&doc);return 0;}
    for(i=0;i<doc.page_count;++i){int rotation=b2026_pdf_page_inherited_integer(&doc,doc.page_objects[i],"/Rotate",0);if(((rotation%360)+360)%360!=0){b2026_pdf_document_free(&doc);return 0;}}
    b2026_pdf_document_free(&doc);return 1;
}

static const char *b2026_pdf_normalize_rotation(const char *operation,int argc,const B2026PdfArg *args){
    int source_index=0,target_rotation=0,rc,i,flatten;const char *source;char *output=NULL,*stage=NULL;HashVariable *settings=NULL;B2026PdfNormalizeSettings normalize;B2026PdfDocument doc;B2026PdfPageList pages;memset(&doc,0,sizeof(doc));memset(&pages,0,sizeof(pages));flatten=strcmp(operation,"FLATTENROTATION")==0;
    if(argc<1||argc>3||args[0].kind!=1)return b2026_pdf_fail(4210,"","rotation normalization requires path, optional output/source, and optional settings / 回転処理の引数が正しくありません");if(argc==2){if(args[1].kind==1)source_index=1;else if(args[1].kind==3){if(!b2026_pdf_tf_hash(&args[1],&settings))return b2026_pdf_fail(4210,"","invalid rotation settings / 回転設定が正しくありません");}else return b2026_pdf_fail(4210,"","invalid rotation arguments / 回転処理の引数が正しくありません");}else if(argc==3){if(args[1].kind!=1||args[2].kind!=3||!b2026_pdf_tf_hash(&args[2],&settings))return b2026_pdf_fail(4210,"","invalid output, source, or rotation settings / 保存先・入力元・回転設定が正しくありません");source_index=1;}
    if(flatten){if(!b2026_pdf_nr_target(settings,&target_rotation))return b2026_pdf_fail(4210,"","rotation must be 0, 90, 180, or 270 / 回転は0、90、180、270のいずれかで指定してください");}
    else{if(!b2026_pdf_nr_target(settings,&target_rotation)||target_rotation!=0||!b2026_pdf_ns_settings(settings,&normalize))return b2026_pdf_fail(4210,"","upright normalization requires rotation 0 and valid A4/page-size settings / 正立方向への統一では回転0と有効な用紙設定を指定してください");}
    source=args[source_index].text;output=b2026_pdf_output_path(args[0].text);if(source==NULL||output==NULL)goto fail;rc=b2026_pdf_load(&doc,source,1);if(rc<=0){b2026_pdf_fail(4211,source,b2026_pdf_load_error(rc));goto fail;}for(i=0;i<doc.page_count;++i){int ok=flatten?b2026_pdf_nr_page(&doc,doc.page_objects[i],target_rotation):b2026_pdf_nr_upright_page(&doc,doc.page_objects[i],&normalize);if(!ok){b2026_pdf_fail(4212,source,"page content, rotation, annotation, or page box could not be transformed / ページ内容・回転・注釈・ページボックスを変換できません");goto fail;}}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,0,0)){if(stage!=NULL)b2026_atomic_discard_file(stage);b2026_pdf_fail(4213,output,"rotation-normalized PDF could not be written / 回転統一PDFを保存できません");goto fail;}if(flatten?!b2026_pdf_nr_verify(stage,target_rotation,doc.page_count):!b2026_pdf_nr_upright_verify(stage,&normalize,doc.page_count)){b2026_atomic_discard_file(stage);b2026_pdf_fail(4213,output,"rotation-normalized PDF failed post-write verification / 回転統一PDFの保存後検証に失敗しました");goto fail;}if(!b2026_atomic_commit_file(stage,output)){b2026_atomic_discard_file(stage);b2026_pdf_fail(4213,output,"verified PDF could not be committed / 検証済みPDFを保存先へ反映できません");goto fail;}free(stage);stage=NULL;
    {const char *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:if(stage!=NULL){b2026_atomic_discard_file(stage);free(stage);}free(output);free(pages.items);b2026_pdf_document_free(&doc);return runtime_temp_string_copy("");
}
