import { attachBoardViewport } from './board-viewport.js';
import { createStudyLevel } from './study-levels.js';
import { TapQueue } from './tap-queue.js';
import { exitTiming } from './exit-animation.js';
import { LEVEL_THEMES, PIGMENTS, MOTION, themeWithColors } from './design-tokens.js';
import { LEVELS } from './levels.js';
import { Game, available, solveRotations, trace } from './core.js';
import { layout, hitCell } from './geometry.js';
import { CanvasPainter, drawScene, drawTile } from './renderer.js';
import { ICONS, ICON_SIZE, ICON_ATLAS } from './ui-skin.js';
import { TILE_ATLAS } from './sprites.js';
const taps=new TapQueue();
const $=id=>document.getElementById(id),canvas=$('preview'),ctx=canvas.getContext('2d'),painter=new CanvasPainter(ctx);
const reduced=matchMedia('(prefers-reduced-motion: reduce)').matches;
let boardViewport;
const regularLevel=createStudyLevel(3);
let studyLevel=createStudyLevel(5);
let colorCount=7;
let theme=themeWithColors(LEVEL_THEMES[0],colorCount),game=new Game(studyLevel),animation=null,rotation=null,frame=0,view=null,hint=null,afterRotation=null;
const atlas=new Image();atlas.src=TILE_ATLAS;atlas.onload=()=>{painter.atlas=atlas;draw();paintSamples();};
function decorateIcons(){
document.querySelectorAll('[data-icon]').forEach(el=>{
const f=ICONS[el.dataset.icon];
el.innerHTML=`
`;
});
}
$('theme-tabs').innerHTML=LEVEL_THEMES.map(t=>``).join('');
$('theme-grid').innerHTML=LEVEL_THEMES.map(t=>``).join('');
$('pigment-grid').innerHTML=PIGMENTS.map((p,i)=>`
${p.name}${p.color}
`).join('');
function setTheme(next){
theme=themeWithColors(next,colorCount);$('phone').style.setProperty('--scene-bg',theme.background);$('density-grid').style.setProperty('--bg',theme.background);$('density-grid').style.setProperty('--ink',theme.ink);$('phone').style.setProperty('--scene-ink',theme.ink);
$('scene-label').textContent=`${theme.label} / ${theme.range}`;$('scene-name').textContent=theme.name;
$('scene-description').textContent=`${theme.description} 当前展示 ${colorCount} 色扩展搭配。`;$('phone-label').textContent=`${theme.label} · ${theme.name}`;
$('background-value').textContent=theme.background;$('background-chip').style.background=theme.background;
$('palette-count').textContent=`${theme.palette.length} / 12`;
$('local-palette').innerHTML=theme.palette.map(i=>``).join('');
document.querySelectorAll('[data-theme]').forEach(b=>b.setAttribute('aria-pressed',String(b.dataset.theme===theme.id)));
for(const id of ['play-theme','game-link'])$(id).href=`index.html?theme=${theme.id}&colors=${colorCount}`;
paintSamples();draw();
}
document.querySelectorAll('[data-theme]').forEach(b=>b.onclick=()=>setTheme(LEVEL_THEMES.find(t=>t.id===b.dataset.theme)));
function fit(c){const rect=c.getBoundingClientRect(),w=rect.width,h=c===canvas?rect.height:w,dpr=Math.min(devicePixelRatio||1,2);c.width=Math.round(w*dpr);c.height=Math.round(h*dpr);const context=c.getContext('2d');context.setTransform(dpr,0,0,dpr,0,0);return {context,width:w,height:h};}
function paintSamples(){
document.querySelectorAll('[data-theme].theme-card>small').forEach(el=>{const t=LEVEL_THEMES.find(t=>t.id===el.parentElement.dataset.theme);el.textContent=`${colorCount} 色棋子 · ${t.background}`;});
document.querySelectorAll('[data-density]').forEach(c=>{const {context,width}=fit(c),p=new CanvasPainter(context);p.atlas=atlas;drawScene(p,studyLevel,studyLevel.tiles,layout(studyLevel.cells,width,width),{theme:themeWithColors(theme,Number(c.dataset.density))});});
document.querySelectorAll('[data-sample]').forEach(c=>{const {context,width}=fit(c),p=new CanvasPainter(context);p.atlas=atlas;const v=layout(studyLevel.cells,width,width);drawScene(p,studyLevel,studyLevel.tiles,v,{theme:themeWithColors(LEVEL_THEMES.find(t=>t.id===c.dataset.sample),colorCount)});});
document.querySelectorAll('[data-pigment]').forEach(c=>{const {context,width}=fit(c),p=new CanvasPainter(context);p.atlas=atlas;drawTile(p,width/2,width/2,width*.46,{color:Number(c.dataset.pigment),dir:1});});
}
function resize(){const {width,height}=fit(canvas);const base=layout(game.level.cells,width,height);view=boardViewport?boardViewport.camera.fit(base,width,height):base;boardViewport?.update();draw();paintSamples();}
function controls(){const busy=!!animation||!!rotation;for(const id of ['demo','left','right','undo','hint'])$(id).disabled=busy; $('undo').disabled=busy||!game.history.length;$('moves').textContent=game.moves;}
function draw(now=performance.now()){
if(!view)return;
cancelAnimationFrame(frame);ctx.clearRect(0,0,canvas.width,canvas.height);
const state={theme,reduced,hint,queued:taps.ids};let done=false,rotated=false;
if(animation){const progress=Math.min(1,(now-animation.start)/animation.duration);state.animation={route:animation.route,progress};done=progress>=1;}
if(rotation){const t=Math.min(1,(now-rotation.start)/rotation.duration);state.boardAngle=-rotation.direction*Math.PI/3*Math.pow(1-t,3);rotated=t>=1;}
drawScene(painter,game.level,game.tiles,view,state);
if(done){const status=animation.route.status;animation=null;$('phone-status').textContent=game.won?'全部离场,漂亮!':status==='exit'?'成功离场 · 可以继续点击棋子':'发生碰撞 · 可撤销或转动底盘';$('motion-status').textContent=status==='exit'?'播放完成 · 再播一次可观察尾迹与碎片':'这条路线被阻挡了';controls();}
if(rotated){rotation=null;controls();const cb=afterRotation;afterRotation=null;if(cb){cb();return;}}
if(!animation&&!rotation){const next=taps.take(game.tiles);if(next){move(next);return;}}
if(animation||rotation)frame=requestAnimationFrame(draw);
}
function reset(){taps.clear();animation=null;rotation=null;afterRotation=null;hint=null;cancelAnimationFrame(frame);game=new Game(studyLevel);$('phone-status').textContent='点击棋子,沿轨道出发';$('motion-status').textContent='可反复播放,也可直接操作棋盘。';controls();draw();}
function move(tile){if(animation||rotation){if(taps.add(tile.id,animation?.route.tile.id))draw();return;}hint=null;const route=game.tap(tile.id);if(!['exit','blocked','loop'].includes(route.status))return;
if(route.status!=='exit')taps.clear();
const base=route.status==='exit'?exitTiming(route).duration:MOTION.collisionMs;
animation={route,start:performance.now(),duration:reduced?150:base*($('slow').checked?2.5:1)};
$('phone-status').textContent=route.status==='exit'?'沿着轨道,找到出口…':'接触后沿原路返回';$('motion-status').textContent=route.status==='exit'?'正在播放:滑行 → 出口收束 → 六角余韵':'正在展示碰撞反馈';controls();draw();
}
function turn(direction,callback=null){if(animation||rotation)return;hint=null;const r=game.rotate(direction);if(r.status!=='rotated')return;afterRotation=callback;rotation={direction,start:performance.now(),duration:reduced?100:MOTION.rotationMs};$('phone-status').textContent='棋子不动,底盘旋转 60°';controls();draw();}
function demonstrate(){
if(animation||rotation)return;
if(game.won||game.moves<=0)reset();
const free=available(game.level,game.tiles);
if(free.length){free.sort((a,b)=>trace(game.level,b,game.tiles).steps.length-trace(game.level,a,game.tiles).steps.length);move(free[0]);return;}
const action=solveRotations({...game.level,tiles:game.tiles,moveLimit:game.moves}).actions[0];
if(action?.type==='rotate')turn(action.direction,demonstrate);else{reset();$('motion-status').textContent='演示已重置,请再次播放。';}
}
$('demo').onclick=demonstrate;$('restart').onclick=reset;
$('left').onclick=()=>turn(-1);$('right').onclick=()=>turn(1);
$('undo').onclick=()=>{if(game.undo()){hint=null;controls();draw();$('phone-status').textContent='已撤销,状态恢复';}};
$('hint').onclick=()=>{const action=solveRotations({...game.level,tiles:game.tiles,moveLimit:game.moves}).actions[0];if(action?.type==='tap'){hint=action.id;$('phone-status').textContent='点击标记的棋子,可以安全离场';draw();}else $('phone-status').textContent=action?.type==='rotate'?`试试${action.direction===1?'右':'左'}转底盘`:'全部完成,可重置演示';};
$('reset').onclick=()=>{taps.clear();$('study-dialog-content').innerHTML='选择关卡
'+LEVELS.map((l,i)=>``).join('')+'
';$('study-dialog').showModal();};
$('next-theme').onclick=()=>{taps.clear();$('study-dialog-content').innerHTML=`设置
`;$('study-dialog').showModal();};
$('study-close').onclick=()=>$('study-dialog').close();
$('study-dialog-content').onclick=e=>{const b=e.target.closest('[data-level]');if(b){studyLevel=LEVELS[Number(b.dataset.level)];document.querySelector('.phone-top strong').textContent=`LEVEL ${String(Number(b.dataset.level)+1).padStart(2,'0')}`;boardViewport.camera.reset();reset();resize();document.querySelectorAll('.density-card .mini-scene>span').forEach(el=>el.textContent=`${el.closest('[data-count]').dataset.count} 色 / ${studyLevel.tiles.length} 枚棋子`);$('study-dialog').close();}if(e.target.closest('#study-fit')){boardViewport.camera.reset();resize();$('study-dialog').close();}};
$('study-dialog-content').onchange=e=>{if(e.target.id==='study-slow')$('slow').checked=e.target.checked;};
boardViewport=attachBoardViewport(canvas,{changed:resize,tap:({x,y})=>{if(!view)return;const cell=hitCell(game.level.cells,view,x,y);const tile=cell&&game.tiles.find(t=>t.q===cell.q&&t.r===cell.r);if(tile)move(tile);}});
$('board-size').onchange=()=>{studyLevel=$('board-size').value==='regular'?regularLevel:createStudyLevel(Number($('board-size').value));boardViewport.camera.reset();reset();resize();document.querySelectorAll('.density-card .mini-scene>span').forEach(el=>el.textContent=`${el.closest('[data-count]').dataset.count} 色 / ${studyLevel.tiles.length} 枚棋子`);};
$('density-grid').innerHTML=[4,7,9,12].map(n=>``).join('');
document.querySelectorAll('[data-count]').forEach(b=>b.onclick=()=>{colorCount=Number(b.dataset.count);document.querySelectorAll('[data-count]').forEach(x=>x.setAttribute('aria-pressed',String(Number(x.dataset.count)===colorCount)));setTheme(LEVEL_THEMES.find(t=>t.id===theme.id));reset();});
document.querySelector('.phone-top strong').textContent='测试关 01';
decorateIcons();setTheme(theme);controls();new ResizeObserver(resize).observe(canvas);