import * as PERMUTE from './permute.js';

import * as CELLINDEX from './cellindex.js';

function index_nodes(nodes, scale) {
	let i = 1;
	for( const n of nodes ) {
		n["id"] = i;
		i++;
	}
}

function scale_nodes(nodes, scale) {
	for( const n of nodes ) {
		for( const a of [ 'x', 'y', 'z', 'w' ] ) {
			n[a] = scale * n[a];
		}
	}
}

function dist2(n1, n2) {
	return (n1.x - n2.x) ** 2 + (n1.y - n2.y) ** 2 + (n1.z - n2.z) ** 2 + (n1.w - n2.w) ** 2;
}

export function auto_detect_edges(nodes, neighbours, debug=false) {
	const seen = {};
	const nnodes = nodes.length;
	const links = [];
	let id = 1;
	for( const n1 of nodes ) {
		const d2 = [];
		for( const n2 of nodes ) {
			d2.push({ d2: dist2(n1, n2), id: n2.id });
		}
		d2.sort((a, b) => a.d2 - b.d2);
		const closest = d2.slice(1, neighbours + 1);
		if( debug ) {
			console.log(`closest = ${closest.length}`);
			console.log(closest);
		}
		for( const e of closest ) {
			const ids = [ n1.id, e.id ];
			ids.sort();
			const fp = ids.join(',');
			if( !seen[fp] ) {
				seen[fp] = true;
				links.push({ id: id, label: 0, source: n1.id, target: e.id });
				id++;
			}
		}
	}
	if( debug ) {
		console.log(`Found ${links.length} edges`)
	}
	return links;
}


export const linkTest = () => {
    return {
        name: 'linky',
        nodes: [
            { id:1, label: 1, x: -1, y: -1, z:-1, w: 0 },
            { id:2, label: 2, x: 1, y: 1, z: 1, w: 0 },
        ],
        links: [
            { id: 1, source: 1, target: 2 }
        ],
  		options: [ { name: '--' }],
		description: `link`,
	  }
};



// too small and simple to calculate



export const cell5 = () => {
	const c1 = Math.sqrt(5) / 4;
	return {
		name: '5-cell',
		nodes: [
	      {id:1, label: 1, x: c1, y: c1, z: c1, w: -0.25 },
	      {id:2, label: 2, x: c1, y: -c1, z: -c1, w: -0.25 },
	      {id:3, label: 3, x: -c1, y: c1, z: -c1, w: -0.25 },
	      {id:4, label: 4, x: -c1, y: -c1, z: c1, w: -0.25 },
	      {id:5, label: 5, x: 0, y: 0, z: 0, w: 1 },
		],
		links: [
			{ id:1, source:1, target: 2},
			{ id:2, source:1, target: 3},
			{ id:3, source:1, target: 4},
			{ id:4, source:1, target: 5},
			{ id:5, source:2, target: 3},
			{ id:6, source:2, target: 4},
			{ id:7, source:2, target: 5},
			{ id:8, source:3, target: 4},
			{ id:9, source:3, target: 5},
			{ id:10, source:4, target: 5},
		],
		options: [ { name: '--' }],
		description: `Five tetrahedra joined at ten faces with three
		tetrahedra around each edge. The 5-cell is the simplest regular
		four-D polytope and the four-dimensional analogue of the tetrahedron.
		A corresponding polytope, or simplex, exists for every n-dimensional
		space.`,
	};
};


export const cell16 = () => {
	let nodes = PERMUTE.coordinates([1, 1, 1, 1],  0);
	nodes = nodes.filter((n) => n.x * n.y * n.z * n.w > 0);

	nodes[0].label = 1;
	nodes[3].label = 2;
	nodes[5].label = 3;
	nodes[6].label = 4;
	nodes[7].label = 1;
	nodes[4].label = 2;
	nodes[2].label = 3;
	nodes[1].label = 4;

	index_nodes(nodes);
	scale_nodes(nodes, 0.5);
	const links = auto_detect_edges(nodes, 6);

	return {
		name: '16-cell',
		nodes: nodes,
		links: links,
		options: [ { name: '--' }],
		description: `Sixteen tetrahedra joined at 32 faces with four
		tetrahedra around each edge. The 16-cell is the four-dimensional
		analogue of the octahedron and is dual to the tesseract. Every
		n-dimensional space has a corresponding polytope in this family.`,
	};
};



export const tesseract = () => {
	const nodes = PERMUTE.coordinates([1, 1, 1, 1],  0);
	index_nodes(nodes);

	for( const n of nodes ) {
		if( n.x * n.y * n.z * n.w > 0 ) {
			n.label = 2;
		} else {
			n.label = 1;
		}
	}

	scale_nodes(nodes, 0.5);
	const links = auto_detect_edges(nodes, 4);
	links.map((l) => { l.label = 0 });

	for( const p of [ 1, 2 ] ) {
		const nodes16 = nodes.filter((n) => n.label === p);
		const links16 = auto_detect_edges(nodes16, 6);
		links16.map((l) => l.label = p);
		links.push(...links16);
	}


	return {
		name: 'Tesseract',
		nodes: nodes,
		links: links,
		options: [
			{ name: 'none', links: [ 0 ] },
			{ name: 'one 16-cell', links: [ 0, 1 ] },
			{ name: 'both 16-cells', links: [ 0, 1, 2 ] },
		],
		description: `The most well-known four-dimensional shape, the
		tesseract is analogous to the cube, and is constructed by placing two
		cubes in parallel hyperplanes and joining their corresponding
		vertices. It consists of eight cubes joined at 32 face with three 
		cubes around each edge, and is dual to the 16-cell. Every
		n-dimensional space has a cube analogue or measure polytope.`,
	};
}


const CELL24_INDEXING = {
	x: { y: 1, z: 3, w: 2 },
	y: { z: 2, w: 3 },
	z: { w: 1 } 
};


function node_by_id(nodes, nid) {
	const ns = nodes.filter((n) => n.id === nid);
	return ns[0];
}


export const cell24 = () => {
	const nodes = PERMUTE.coordinates([0, 0, 1, 1], 0);
	
	for( const n of nodes ) {
		const axes = ['x', 'y', 'z', 'w'].filter((a) => n[a] !== 0);
		n.label = CELL24_INDEXING[axes[0]][axes[1]];
	}

	scale_nodes(nodes, Math.sqrt(2) / 2);
	index_nodes(nodes);
	const links = auto_detect_edges(nodes, 8);
	links.map((l) => l.label = 0);

	for( const p of [ 1, 2, 3 ] ) {
		const nodes16 = nodes.filter((n) => n.label === p);
		const links16 = auto_detect_edges(nodes16, 6);
		links16.map((l) => l.label = p);
		links.push(...links16);
	}
	// links.map((l) => {
	// 	const ls = [ l.source, l.target ].map((nid) => node_by_id(nodes, nid).label);
	// 	for ( const c of [1, 2, 3] ) {
	// 		if( ! ls.includes(c) ) {
	// 			l.label = c
	// 		}
	// 	}
	// });

	return {
		name: '24-cell',
		nodes: nodes,
		links: links,
		base: {},
		options: [
			{ name: 'none', links: [ 0 ] },
			{ name: 'one 16-cell', links: [ 0, 1 ] },
			{ name: 'three 16-cells', links: [ 0, 1, 2, 3 ] }
		],
		description: `A unique object without an exact analogue in higher
		or lower dimensions, the 24-cell is made of twenty-four octahedra
		joined at 96 faces, with three around each edge. The 24-cell is
		self-dual.`,
	};
}





// face detection for the 120-cell

// NOTE: all of these return node ids, not nodes

// return all the links which connect to a node

function nodes_links(links, nodeid) {
	return links.filter((l) => l.source === nodeid || l.target === nodeid);
}

// filter to remove a link to a given id from a set of links

function not_to_this(link, nodeid) {
	return !(link.source === nodeid || link.target === nodeid);
}

// given nodes n1, n2, return all neighbours of n2 which are not n1

function unmutuals(links, n1id, n2id) {
	const nlinks = nodes_links(links, n2id).filter((l) => not_to_this(l, n1id));
	return nlinks.map((l) => {
		if( l.source === n2id ) {
			return l.target;
		} else {
			return l.source;
		}
	})
}


function fingerprint(ids) {
	const sids = [...ids];
	sids.sort();
	return sids.join(',');
}



function auto_120cell_faces(links) {
	const faces = [];
	const seen = {};
	let id = 1;
	for( const edge of links ) {
		const v1 = edge.source;
		const v2 = edge.target;
		const n1 = unmutuals(links, v2, v1);
		const n2 = unmutuals(links, v1, v2);
		const shared = [];
		for( const a of n1 ) {
			const an = unmutuals(links, v1, a);
			for( const d of n2 ) {
				const dn = unmutuals(links, v2, d);
				for( const x of an ) {
					for( const y of dn ) {
						if( x == y ) {
							shared.push([v1, a, x, d, v2])
						}
					}
				}
			}
		}
		if( shared.length !== 3 ) {
			console.log(`Bad shared faces for ${edge.id} ${v1} ${v2}`);
		}
		for( const face of shared ) {
			const fp = fingerprint(face);
			if( !seen[fp] ) {
				faces.push({ id: id, nodes: face });
				id++;
				seen[fp] = true;
			}
		}
	}
	return faces;
}



export function make_120cell_vertices() {
	const phi = 0.5 * (1 + Math.sqrt(5));  
	const r5 = Math.sqrt(5);   
	const phi2 = phi * phi;    
	const phiinv = 1 / phi;    
	const phi2inv = 1 / phi2;  

	const nodes = [
		PERMUTE.coordinates([0, 0, 2, 2],  0),
		PERMUTE.coordinates([1, 1, 1, r5], 0),
		PERMUTE.coordinates([phi, phi, phi, phi2inv], 0),
		PERMUTE.coordinates([phiinv, phiinv, phiinv, phi2], 0),

		PERMUTE.coordinates([phi2, phi2inv, 1, 0], 0, true),
		PERMUTE.coordinates([r5, phiinv, phi, 0], 0, true),
		PERMUTE.coordinates([2, 1, phi, phiinv], 0, true),
		].flat();
	index_nodes(nodes);
	scale_nodes(nodes, 0.25 * Math.sqrt(2));
	return nodes;
}





function label_nodes(nodes, ids, label) {
	nodes.filter((n) => ids.includes(n.id)).map((n) => n.label = label);
}




function label_faces_120cell(nodes, faces, cfaces, label) {
	const ns = new Set();
	for( const fid of cfaces ) {
		const face = faces.filter((f)=> f.id === fid );
		if( face.length > 0 ) {
			for ( const nid of face[0].nodes ) {
				ns.add(nid);
			}
		}
	}
	label_nodes(nodes, Array.from(ns), label);
}


function link_labels(nodes, link) {
	const n1 = nodes.filter((n) => n.id === link.source);
	const n2 = nodes.filter((n) => n.id === link.target);
	return [ n1[0].label, n2[0].label ];
}


// version of the 120-cell where nodes are partitioned by 
// layer and the links follow that

export const cell120_layered = (max) => {
	const nodes  = make_120cell_vertices();
	const links = auto_detect_edges(nodes, 4);

	nodes.map((n) => n.label = 9); // make all invisible by default

	for (const cstr in CELLINDEX.LAYERS120 ) {
		label_nodes(nodes, CELLINDEX.LAYERS120[cstr], Number(cstr));
	}

	links.map((l) => {
		const labels = link_labels(nodes, l);
		if( labels[0] >= labels[1] ) {
			l.label = labels[0];
		} else {
			l.label = labels[1];
		}
	});

	const options = [];
	const layers = [];

	for( const i of [ 0, 1, 2, 3, 4, 5, 6, 7 ] ) {
		layers.push(i);
		options.push({
			name: CELLINDEX.LAYER_NAMES[i],
			links: [...layers],
			nodes: [...layers]
		})
	}

	return {
		name: '120-cell layered',
		nodes: nodes,
		links: links,
		nolink2opacity: true,
		options: options,
		description: `This version of the 120-cell lets you explore its
		structure by building each layer from the 'north pole' onwards.`,
	}
}




export const cell120_inscribed = () => {
	const nodes  = make_120cell_vertices();
	const links = auto_detect_edges(nodes, 4);

  	for( const cstr in CELLINDEX.INDEX120 ) {
		label_nodes(nodes, CELLINDEX.INDEX120[cstr], Number(cstr));
	}

	links.map((l) => l.label = 0);

	for( const p of [ 1, 2, 3, 4, 5 ]) {
		const nodes600 = nodes.filter((n) => n.label === p);
		const links600 = auto_detect_edges(nodes600, 12);
		links600.map((l) => l.label = p);
		links.push(...links600);
	}

        const CELL5S = CELLINDEX.CELL120_CELL5.cell5s;

        for( const c5 in CELL5S ) {
            const nodes5 = nodes.filter((n) => CELL5S[c5].includes(n.id));
            const links5 = auto_detect_edges(nodes5, 5);
            links5.map((l) => l.label = 8);
            links.push(...links5);
        }

	return {
		name: '120-cell',
		nodes: nodes,
		links: links,
		options: [
			{ name: "none", links: [ 0 ]},
			{ name: "one inscribed 600-cell", links: [ 0, 1 ] },
			{ name: "five inscribed 600-cells", links: [ 0, 1, 2, 3, 4, 5 ] },
                        { name: "120 inscribed 5-cells", links: [ 0, 8 ] },
		],
		description: `The 120-cell is the four-dimensional analogue of the
		dodecahedron, and consists of 120 dodecahedra joined at 720 faces,
		with three dodecahedra around each edge. It is dual to the 600-cell,
		and five 600-cells can be inscribed in its vertices. The converse
		of this allows 120 5-cells (each of which has one vertex in each
                of the 5 600-cells) to be inscribed in the 120-cell.`,
	}
}



export const cell120_inscribed_cell5 = () => {
	const nodes  = make_120cell_vertices();
	const links = [];

  	for( const cstr in CELLINDEX.INDEX120 ) {
		label_nodes(nodes, CELLINDEX.INDEX120[cstr], Number(cstr));
	}

	links.map((l) => l.label = 0);

        const CELL5S = CELLINDEX.CELL120_CELL5.cell5s;

        for( const c5 in CELL5S ) {
            const nodes5 = nodes.filter((n) => CELL5S[c5].includes(n.id));
            const links5 = auto_detect_edges(nodes5, 5);
            links5.map((l) => l.label = Number(c5));
            links.push(...links5);
        }

        const show_links = Array.from({ length: 128 }, (_, i) => i);

	return {
		name: '120 5-cells',
		nodes: nodes,
		links: links,
		options: [
			{ name: "none", links: show_links},
	        ],
		description: `The 120 5-cells from the 120-cell, without the latter's links. This colouring is pretty arbitrary, being based on the algorithm which partitioned the nodes: a later version will have something that's based on the symmetries of the 600-cells which each of the 5-cells has its nodes in.`,
	}
}


export const cell120_inscribe_cell5_subset = () => {
	const nodes  = make_120cell_vertices();
	const links = auto_detect_edges(nodes, 4);

  	for( const cstr in CELLINDEX.INDEX120 ) {
		label_nodes(nodes, CELLINDEX.INDEX120[cstr], Number(cstr));
	}

	links.map((l) => l.label = 0);

        const CELL5S = CELLINDEX.CELL120_CELL5.some_cell5s;
        const link5s = [];
        const nodes_subset = [];

        for( const c5 in CELL5S ) {
            const nodes5 = nodes.filter((n) => CELL5S[c5].includes(n.id));
            const cell5links = auto_detect_edges(nodes5, 5);
            cell5links.map((l) => l.label = 8);
            link5s.push(...cell5links);
            for( const n5 of nodes5 ) {
               nodes_subset.push(n5);
            }
        }

        // now add the links of the inscribed 600-cells which have the 
        // node subset in them 

        const link_subset = (l) => {
            const source = nodes_subset.filter((n) => n.id === l.source);
            const target = nodes_subset.filter((n) => n.id === l.target);
            return source.length === 1 && target.length === 1;
        };

	for( const p of [ 1, 2, 3, 4, 5 ]) {
	    const nodes600 = nodes.filter((n) => n.label === p);
	    const links600 = auto_detect_edges(nodes600, 12);
    	    links600.map((l) => l.label = p);
	    for( const link of links600 ) {
    	        if( link_subset(link) ) {
	            link5s.push(link);
	        }
	    }
        }
        console.log(link5s);

	return {
		name: '120-cell 5-cell subset',
		nodes: nodes_subset,
		links: link5s,
		options: [
			{ name: "none", links: [ 0, 1, 2, 3, 4, 5, 8 ]},
		],
		description: `Showing the subset of 5-cells in one icosahedron`,
	}
}

function partition_coord(i, coords, invert) {
	const j = invert ? -i : i;
	if( j >= 0 ) {
		return coords[j];
	}
	return "-" + coords[-j];
}

function partition_fingerprint(n, coords, invert) {
	const p = ['x','y','z','w'].map((a) => partition_coord(n[a], coords, invert));
	const fp = p.join(',');
	return fp;
}


function label_vertex(n, coords, partition) {
	const fp = partition_fingerprint(n, coords, false);
	if( fp in partition ) {
		return partition[fp];
	} else {
		const ifp = partition_fingerprint(n, coords, true);
		if( ifp in partition ) {
			return partition[ifp];
		}
		console.log(`Map for ${fp} ${ifp} not found`);
		return 0;
	}
}


function map_coord(i, coords, values) {
	if( i >= 0 ) {
		return values[coords[i]];
	}
	return -values[coords[-i]];
}


export function make_600cell_vertices() {
	const coords = {
		0: '0',
		1: '1',
		2: '2',
		3: 't',
		4: 'k'
	};
	const t = 0.5 * (1 + Math.sqrt(5));
	const values = {
		'0': 0,
		'1': 1,
		'2': 2,
		't': t,
		'k': 1 / t 
	};

	const nodes = [
		PERMUTE.coordinates([0, 0, 0, 2],  0),
		PERMUTE.coordinates([1, 1, 1, 1],  0),
		PERMUTE.coordinates([3, 1, 4, 0],  0, true)
	].flat();

	for( const n of nodes ) {
		n.label = label_vertex(n, coords, CELLINDEX.PARTITION600);
	}

	for( const n of nodes ) {
		for( const a of [ 'x', 'y', 'z', 'w'] ) {
			n[a] = map_coord(n[a], coords, values);
		}
	}


	index_nodes(nodes);
	scale_nodes(nodes, 0.5);
	return nodes;
}

function get_node(nodes, id) {
	const ns = nodes.filter((n) => n.id === id);
	if( ns ) {
		return ns[0]
	} else {
		return undefined;
	}
}

function audit_link_labels(nodes, links) {
	for( const l of links ) {
		const n1 = get_node(nodes, l.source);
		const n2 = get_node(nodes, l.target);
		if( n1.label === n2.label ) {
			console.log(`link ${l.id} joins ${n1.id} ${n2.id} with label ${n2.label}`);
		}
	}
}



export const cell600 = () => {
	const nodes  = make_600cell_vertices();
	const links = auto_detect_edges(nodes, 12);

	links.map((l) => l.label = 0);

	for( const p of [1, 2, 3, 4, 5]) {
		const nodes24 = nodes.filter((n) => n.label === p);
		const links24 = auto_detect_edges(nodes24, 8);
		links24.map((l) => l.label = p);
		links.push(...links24);
	}

	return {
		name: '600-cell',
		nodes: nodes,
		links: links,
		options: [
			{ name: "none", links: [ 0 ]},
			{ name: "one 24-cell", links: [ 0, 1 ] },
			{ name: "five 24-cells", links: [ 0, 1, 2, 3, 4, 5 ] }
		],
		description: `The 600-cell is the four-dimensional analogue of the
		icosahedron, and consists of 600 tetrahedra joined at 1200 faces
		with five tetrahedra around each edge. It is dual to the 120-cell.
		Its 120 vertices can be partitioned into five sets which form the
		vertices of five inscribed 24-cells.`,
	}
}


export const cell600_layered = () => {
	const nodes  = make_600cell_vertices();
	const links = auto_detect_edges(nodes, 12);

	nodes.map((n) => n.label = 9); // make all invisible by default

	for (const cstr in CELLINDEX.LAYERS600 ) {
		label_nodes(nodes, CELLINDEX.LAYERS600[cstr], Number(cstr));
	}

	links.map((l) => {
		const labels = link_labels(nodes, l);
		if( labels[0] >= labels[1] ) {
			l.label = labels[0];
		} else {
			l.label = labels[1];
		}
	});

	const options = [];
	const layers = [];

	for( const i of [ 0, 1, 2, 3, 4, 5, 6, 7 ] ) {
		layers.push(i);
		options.push({
			name: CELLINDEX.LAYER_NAMES[i],
			links: [...layers],
			nodes: [...layers]
		})
	}

	return {
		name: '600-cell layered',
		nodes: nodes,
		links: links,
		nolink2opacity: true,
		options: options,
		description: `This version of the 600-cell lets you explore its
		structure by building each layer from the 'north pole' onwards.`,
	}


}


export const snub24cell = () => {
	const nodes600  = make_600cell_vertices();
	const links600 = auto_detect_edges(nodes600, 12);

	const nodes = nodes600.filter((n) => n.label != 1);
	const links = links600.filter((l) => {
		const sn = node_by_id(nodes, l.source);
		const tn = node_by_id(nodes, l.target);
		return sn && tn;
	});

	links.map((l) => l.label = 0);

	return {
		name: 'Snub 24-cell',
		nodes: nodes,
		links: links,
		options: [ { name: "--" } ],
		description: `The snub 24-cell is a semiregular polytope which
		connects the 24-cell with the 600-cell. It consists of 24 icosahedra
		and 120 tetrahedra, and is constructed by removing one of the
		five inscribed 24-cells from a 600-cell.`

	}


}





function make_dodecahedron_vertices() {
	const phi = 0.5 * (1 + Math.sqrt(5));  
	const phiinv = 1 / phi;    

	const nodes = [
			{ x: 1, y: 1, z: 1, w: 0, 			label: 4 },
			{ x: 1, y: 1, z: -1, w: 0, 			label: 3 },
			{ x: 1, y: -1, z: 1, w: 0, 			label: 3 },
			{ x: 1, y: -1, z: -1, w: 0, 		label: 2 },

			{ x: -1, y: 1, z: 1, w: 0, 			label: 3 },
			{ x: -1, y: 1, z: -1, w: 0, 		label: 1 },
			{ x: -1, y: -1, z: 1, w: 0, 		label: 5 },
			{ x: -1, y: -1, z: -1, w: 0, 		label: 3 },

			{ x: 0, y: phi, z: phiinv, w: 0, 	label: 5 },
			{ x: 0, y: phi, z: -phiinv, w: 0 , 	label: 2 },
			{ x: 0, y: -phi, z: phiinv, w: 0, 	label: 4 },
			{ x: 0, y: -phi, z: -phiinv, w: 0 , label: 1 },

			{ x: phiinv, y: 0, z: phi, w: 0 , 	label: 2},
			{ x: phiinv, y: 0, z: -phi, w: 0 , 	label: 4},
			{ x: -phiinv, y: 0, z: phi, w: 0 , 	label: 1},
			{ x: -phiinv, y: 0, z: -phi, w: 0 , label: 5},

			{ x: phi, y: phiinv, z:0, w: 0 , 	label: 1},
			{ x: phi, y: -phiinv, z:0, w: 0 , 	label: 5},
			{ x: -phi, y: phiinv, z:0, w: 0 , 	label: 4},
			{ x: -phi, y: -phiinv, z:0, w: 0 , 	label: 2},
		];
	scale_nodes(nodes, 1 / Math.sqrt(3));
	index_nodes(nodes);
	return nodes;
}

export const dodecahedron = () => {
	const nodes  = make_dodecahedron_vertices();
	const links = auto_detect_edges(nodes, 3);
	links.map((l) => l.label = 0);

	for( const p of [ 1, 2, 3, 4, 5 ]) {
		const tetran = nodes.filter((n) => n.label === p);
		const tetral = auto_detect_edges(tetran, 3);
		tetral.map((l) => l.label = p);
		links.push(...tetral);
	}

	return {
		name: 'Dodecahedron',
		nodes: nodes,
		links: links,
		options: [
			{ name: "none", links: [ 0 ]},
			{ name: "one tetrahedron", links: [ 0, 1 ] },
			{ name: "five tetrahedra", links: [ 0, 1, 2, 3, 4, 5 ] }
		],
		description: `The dodecahedron is a three-dimensional polyhedron
		which is included here so that you can see the partition of its
		vertices into five interlocked tetrahedra. This structure is the
		basis for the partition of the 120-cell's vertices into five
		600-cells.`

	}
}


export const tetrahedron = () => {
	const r2 = Math.sqrt(2);
	const r3 = Math.sqrt(3);
	return {
		name: 'Tetrahedron',
		nodes: [
	      {id:1, label: 1, x: 2 * r2 / 3, y: 0, z: -1/3, w: 0 },
	      {id:2, label: 2, x: -r2 / 3, y: r2 / r3, z: -1/3, w: 0 },
	      {id:3, label: 3, x: -r2 / 3, y: -r2 / r3, z: -1/3, w: 0 },
	      {id:4, label: 4, x: 0, y: 0, z: 1, w: 0 },
		],
		links: [
			{ id:1, source:1, target: 2},
			{ id:2, source:1, target: 3},
			{ id:3, source:1, target: 4},
			{ id:4, source:2, target: 3},
			{ id:5, source:2, target: 4},
			{ id:6, source:3, target: 4},
		],
		options: [ { name: '--' }],
		description: `The simplest three-dimensional polytope, consisting of four triangles joined at six edges. The 5-cell is its four-dimensional analogue.`,
	};
};

export const octahedron = () => {
	const nodes = [
		{id: 1, label: 1, x: 1, y: 0, z: 0, w: 0},
		{id: 2, label: 1, x: -1, y: 0, z: 0, w: 0},
		{id: 3, label: 2, x: 0, y: 1, z: 0, w: 0},
		{id: 4, label: 2, x: 0, y: -1, z: 0, w: 0},
		{id: 5, label: 3, x: 0, y: 0, z: 1, w: 0},
		{id: 6, label: 3, x: 0, y: 0, z: -1, w: 0},
	];
	const links = [
		{id:1, source: 1, target: 3},
		{id:2, source: 1, target: 4},
		{id:3, source: 1, target: 5},
		{id:4, source: 1, target: 6},
		{id:5, source: 2, target: 3},
		{id:6, source: 2, target: 4},
		{id:7, source: 2, target: 5},
		{id:8, source: 2, target: 6},
		{id:9, source: 3, target: 5},
		{id:10, source: 3, target: 6},
		{id:11, source: 4, target: 5},
		{id:12, source: 4, target: 6},
	]
	links.map((l) => { l.label = 0 });
	return {
		name: 'Octahedron',
		nodes: nodes,
		links: links,
		options: [ { name: '--' }],
		description: `The three-dimensional cross-polytope, the 16-cell is its four-dimensional analogue.`,
	};

}

export const cube = () => {
	const nodes = [
		{id: 1, label: 1, x: 1, y: 1, z: 1, w: 0},
		{id: 2, label: 2, x: -1, y: 1, z: 1, w: 0},
		{id: 3, label: 2, x: 1, y: -1, z: 1, w: 0},
		{id: 4, label: 1, x: -1, y: -1, z: 1, w: 0},
		{id: 5, label: 2, x: 1, y: 1, z: -1, w: 0},
		{id: 6, label: 1, x: -1, y: 1, z: -1, w: 0},
		{id: 7, label: 1, x: 1, y: -1, z: -1, w: 0},
		{id: 8, label: 2, x: -1, y: -1, z: -1, w: 0},
	];
	scale_nodes(nodes, 1/Math.sqrt(3));
	const links = auto_detect_edges(nodes, 3);
	links.map((l) => { l.label = 0 });
	return {
		name: 'Cube',
		nodes: nodes,
		links: links,
		options: [ { name: '--' }],
		description: `The three-dimensional measure polytope, the tesseract is its four-dimensional analogue.`,
	};

}



function make_icosahedron_vertices() {
	const phi = 0.5 * (1 + Math.sqrt(5));  

	const nodes = [
		{ x: 0, y: 1, z: phi, w: 0, label: 1 },
		{ x: 0, y: -1, z: phi, w: 0, label: 1 },
		{ x: 0, y: 1, z: -phi, w: 0, label: 1 },
		{ x: 0, y: -1, z: -phi, w: 0, label: 1 },
		{ x: 1, y: phi, z: 0, w: 0, label: 2 },
		{ x: -1, y: phi, z: 0, w: 0, label: 2 },
		{ x: 1, y: -phi, z: 0, w: 0, label: 2 },
		{ x: -1, y: -phi, z: 0, w: 0, label: 2 },
		{ x: phi, y: 0, z: 1, w: 0, label: 3},
		{ x: phi, y: 0, z: -1, w: 0, label: 3},
		{ x: -phi, y: 0, z: 1, w: 0, label: 3},
		{ x: -phi, y: 0, z: -1, w: 0, label: 3},
		];

   	scale_nodes(nodes, 1/Math.sqrt((5 + Math.sqrt(5)) / 2));
	index_nodes(nodes);
	return nodes;
}



export const icosahedron = () => {
	const nodes  = make_icosahedron_vertices();
	const links = auto_detect_edges(nodes, 5);
	links.map((l) => l.label = 0);

	return {
		name: 'Icosahedron',
		nodes: nodes,
		links: links,
		options: [
			{ name: "--"},
		],
		description: `The icosahedron is a twenty-sided polyhedron and is dual to the dodecahedron. Its four-dimensional analogue is the 600-cell.`

	}
}


export const build_all = () => {
	return [
		tetrahedron(),
		octahedron(),
		cube(),
		icosahedron(),
	 	dodecahedron(),
	 	cell5(),
		cell16(),
		tesseract(),
		cell24(),
		snub24cell(),
		cell600(),
		cell600_layered(),
	 	cell120_inscribed(),
	 	cell120_inscribed_cell5(),
	 	cell120_inscribe_cell5_subset(),
	 	cell120_layered()
	 	];
}

export const radii = (shape) => {
	return shape.nodes.map(n => Math.sqrt(n.x * n.x + n.y * n.y + n.z * n.z + n.w * n.w))
}
